| Författare |
Inlägg |
|
|
krizsan
Blev medlem: Jan 10, 2007
Inlägg: 622
Offline
|
Hej!
Spring Roo (http://www.springsource.org/roo) är inte bara ett verktyg för att öka produktiviteten hos Javaprogrammerare. Koden som genereras är, i mitt tycke, intressant ur arkitektonisk synpunkt.
Arkitekturen som används i program genererade av Spring Roo eliminerar helt dataåtkomst-lagret genom att introducera kod som hanterar lagring i domän-klasser med hjälp av AspectJ. Det finns alltså inga separata DAO-klasser, som annars är brukligt.
Separationen av olika aspekter av en klass i separata filer med hjälp av AspectJ resulterar i mycket ren kod.
I falled med Spring Roo så används också detta för att låta verktyget vara ansvarig för hanteringen av vissa filer som det genererar (AspectJ filerna) medan Java klasser kan modifieras för hand av programmeraren. Den handskrivna koden i en Java klass bevaras när verktyget genererar om kod, medan koden i relaterade AspectJ filer ersätts.
För att klargöra så visar jag ett exempel där en klass Customer består av tre filer; en Java klass och två AspectJ filer. Java-klassen innehåller fält som är relaterade till affärs-funktionaliteten (direktöversatt från ”business functionality”) hos klassen i fråga:
@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Customer {
/* Varje kund har ett namn som inte får vara null. */
@NotNull
private String name;
/* Varje kund har ett postnummer som består av tre eller flera siffror. */
@NotNull
@Size(min = 3)
@Pattern(regexp = "[0-9]*")
private String zipcode;
/* Varje kund har ett antal ordrar. */
@OneToMany(targetEntity = MyOrder.class)
@JoinColumn
private Collection<MyOrder> orders;
// Etc. Övriga fält borttagna för att korta ner exemplet.
} |
En av de av Roo genererade AspectJ källkodsfilerna innehåller så kallade getters och setters för fälten i en Customer:
privileged aspect Customer_Roo_JavaBean {
public String Customer.getName() {
return this.name;
}
public void Customer.setName(String name) {
this.name = name;
}
// Etc. Övriga metoder har utelämnats för att korta ner exemplet.
}
|
Utöver detta så genererar Roo också en AspectJ fil som innehåller implementationen av toString metoden för Customer. Koden underhålles, som tidigare, av Roo; om du lägger till ett fält i Customer så uppdaterar Roo koden i toString metoden så att innehållet i det nya fältet också skrivs ut.
En annan AspectJ fil innehåller kod och fält relaterade till lagring av instanser av Customer klassen i databas. Notera hur två fält, id och version, har introducerats i Customer klassen av denna Aspect. Dessa två fält är inte relaterade till affärs-funktionaliteten hos ett Customer objekt, utan används endast när ett objekt skall lagras. Följdaktligen hålles dessa fält separerade från Customer.java filen. Hela AspectJ filen som innehåller kod och fält relaterade till lagring ser ut såhär:
privileged aspect Customer_Roo_Entity {
@PersistenceContext
transient EntityManager Customer.entityManager;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long Customer.id;
@Version
@Column(name = "version")
private Integer Customer.version;
public Long Customer.getId() {
return this.id;
}
public void Customer.setId(Long id) {
this.id = id;
}
public Integer Customer.getVersion() {
return this.version;
}
public void Customer.setVersion(Integer version) {
this.version = version;
}
@Transactional
public void Customer.persist() {
if (this.entityManager == null) this.entityManager = entityManager();
this.entityManager.persist(this);
}
@Transactional
public void Customer.remove() {
if (this.entityManager == null) this.entityManager = entityManager();
if (this.entityManager.contains(this)) {
this.entityManager.remove(this);
} else {
Customer attached = this.entityManager.find(Customer.class, this.id);
this.entityManager.remove(attached);
}
}
@Transactional
public void Customer.flush() {
if (this.entityManager == null) this.entityManager = entityManager();
this.entityManager.flush();
}
@Transactional
public void Customer.merge() {
if (this.entityManager == null) this.entityManager = entityManager();
Customer merged = this.entityManager.merge(this);
this.entityManager.flush();
this.id = merged.getId();
}
public static final EntityManager Customer.entityManager() {
EntityManager em = new Customer().entityManager;
if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
return em;
}
public static long Customer.countCustomers() {
return (Long) entityManager().createQuery("select count(o) from Customer o").getSingleResult();
}
public static List<Customer> Customer.findAllCustomers() {
return entityManager().createQuery("select o from Customer o").getResultList();
}
public static Customer Customer.findCustomer(Long id) {
if (id == null) return null;
return entityManager().find(Customer.class, id);
}
public static List<Customer> Customer.findCustomerEntries(int firstResult, int maxResults) {
return entityManager().createQuery("select o from Customer o").setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
}
}
|
Utöver ovanstående så kan Roo skapa vy och kontroller för CRUD (Create Read Update Delete) operationer på Customer objekt. MVC mönstret implementeras av Spring MVC och vy-delen är vanlig JSP.
Roo hjälper också till med att förbereda konfiguration för lagring i databas med Hibernate, EclipseLink eller JPA.
Spring Roo är ett trevligt verktyg men den stora behållningen för mig är hur olika aspekter separeras med hjälp av AspectJ. Om en klass som Customer används både på server och klient sidan i en applikation kan implementationen som vanligtvis läggs i ett gemensamt bibliotek hållas till ett minimum. Funktionalitet som används på server-sidan implementeras sedan i en Aspect som bara finns på just server-sidan och funktionalitet som används på klient-sidan implementeras i en Aspect som bara finns på klient-sidan. På detta sätt separeras de olika aspekterna av en Customer mycket tydligt.
Slutligen vill jag också nämna att Spring Roo bara använder sig av standard Java samt AspectJ för att åstadkomma ovanstående. Inga specialskrivna bibliotek eller liknande behövs.
Koden i AspectJ filerna vävs in i Java koden när programmet kompileras så, till skillnad från dynamiska språk som Groovy och Ruby, inget extraarbete som eventuellt saktar ner programmet behövs utföras vid körning.
Detta var bara en mycket kort introduktion till Spring Roo. Det finns så mycket bra material på internet så jag nöjer mig med detta.
Trevlig programmering!
|
|
|
 |
|
|
|
|