跳至主要内容

More fun with Spring Roo

more fun with roo

We have created a Spring web project using Spring Roo, now let us have a look at the generated codes.

Import codes into Eclipse workspace

If you created the project from Roo console, you can follow the following steps to import the project codes into your Eclipse workspace.
If you created the project from Eclipse directly, skip this section.
  1. Start Eclipse IDE.
  2. Select Import... menu item from File menu in the Eclipse main menu.
  3. In the popup Import... dialog, expand the Maven node, and select Existing Maven Projects and click Next button.
  4. Choose the created project folder, it will scan the project files, and recognize the pom.xml file, select it by checking the checkbox, and click Finish button.
Please keep patient, and wait for one or two minutes. After the project is imported Eclipse will resolve the dependencies automatically.

Explore the project codes

Switch to Spring perspective, and open Package view.
In the Package view, click the array icon besides project node of the imported project to expand all nodes of the project.
It is a standard Maven web project.
src/main/java includes all java source files, and all configuration files of the project are placed in the src/main/resource, including the Spring specific configuration files, they will be included in the /WEB-INF/classes in the final deployment war structure. src/test/java includes the test files, and src/main/webapp includes all web resources. In these nodes, src/main/java and src/test/java are recognized as package nodes and displayed as package view.
Expand src/main/java source package node, you will see some packages we have already created in previous steps.
  1. ~.model contains the JPA entity classes.
  2. ~.repository includes the classes of the repository layer.
  3. ~.service includes the classes of the service layer.
  4. ~.controller includes the web controller classes for the presentation layer.
Expand the ~.model package node.
Open the entity class Conference, you will find three annotations defined on the class, @RooJavaBean, @RooToString, @RooJpaEntity, they mean some aspects applied on the class.
@RooJavaBean
@RooToString
@RooJpaEntity
public class Conference {
    //fields
}
And in the same package, you must see other AspectJ specific aspect files which are generated by Spring Roo, Conference_Roo_JavaBean.aj, Conference_Roo_Jpa_Entity.aj, Conference_Roo_ToString.aj .
privileged aspect Conference_Roo_JavaBean {
    
    public String Conference.getName() {
        return this.name;
    }
    
    public void Conference.setName(String name) {
        this.name = name;
    }
    //setters and getters of other fields.
}
Aligned with @RooJavaBean annotation on the Conference class, Conference_Roo_JavaBean.aj will contribute the general JavaBean properties(setters and getters excluding the @Id and @Version properties) to the entity Conference.
privileged aspect Conference_Roo_ToString {
    
    public String Conference.toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
    
}
@RooToString and Conference_Roo_ToString.aj will override the toString method of Conference class, it used ReflectionToStringBuilder which is from the Apache Commons Lang project.
privileged aspect Conference_Roo_Jpa_Entity {
    
    declare @type: Conference: @Entity;
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long Conference.id;
    
    @Version
    @Column(name = "version")
    private Integer Conference.version;
    
    public Long Conference.getId() {
        return this.id;
    }
    
    public void Conference.setId(Long id) {
        this.id = id;
    }
    
    public Integer Conference.getVersion() {
        return this.version;
    }
    
    public void Conference.setVersion(Integer version) {
        this.version = version;
    }
    
}
@RooJpaEntity and Conference_Roo_Jpa_Entity.aj will contribute the annotation @Entity to Conference and also manage the entity related properties, such as @Id, @Version.
We have also created a ConferenceRepository class in the ~.repository package and ConferenceService class in the ~.service package for the entity Conference. ConferenceRepository is a empty interface with an annotation added on the class definition @RooJpaRepository(domainType = Conference.class), ConferenceRepository_Roo_Jpa_Repository includes the database operations, it reused the effort from Spring Data subproject. ConferenceService and ConferenceService_Roo_Service include all business logic declaration, and ConferenceServiceImpl and ConferenceServiceImpl_Roo_Service include the certain implementation.
privileged aspect ConferenceRepository_Roo_Jpa_Repository {
    
    declare parents: ConferenceRepository extends JpaRepository;
    
    declare parents: ConferenceRepository extends JpaSpecificationExecutor;
    
    declare @type: ConferenceRepository: @Repository;
    
}
Obviously, this aspect will add annotation @Repository on class ConferenceRepository and make ConferenceRepository extend JpaRepository and JpaSpecificationExecutor interfaces, which are from the Spring Data JPA project.
In the ~.service package, it also includes related aspects for ConferenceService interface and implementation class ConferenceServiceImpl.
All abstract methods of interface ConferenceService are included in the aspect ConferenceService_Roo_Service.
And in the aspect ConferenceServiceImpl_Roo_Service, it provides the operations of the implementation class, show as below.
privileged aspect ConferenceServiceImpl_Roo_Service {
    
    declare @type: ConferenceServiceImpl: @Service;
    
    declare @type: ConferenceServiceImpl: @Transactional;
    
    @Autowired
    ConferenceRepository ConferenceServiceImpl.conferenceRepository;
    
}
Annotations @Service and @Transactional are added and ConferenceRepository is injected for ConferenceServiceImpl.
In this example, we used --activeRecord=false to generate the codes, if you are using activeRecord(use --activeRecord=true or omit this option), the codes will be a little different.
I have create another entity class named Signup, which used ActiveRecord pattern. You will find there is another annotation @RooJpaActiveRecord on the Signup entity class instead of @RooJpaEntity, and two more AspectJ aspect files are generated in the same package, Signup_Roo_Configurable and Signup_Roo_Jpa_ActiveRecord.
privileged aspect Signup_Roo_Configurable {
    
    declare @type: Signup: @Configurable;
    
}
This aspect will add another @Configurable annotation on the entity class, this make the entity is injectable, and you can use @PersisteneceContext to inject EntityManager in an entity.
privileged aspect Signup_Roo_Jpa_ActiveRecord {
    
    @PersistenceContext
    transient EntityManager Signup.entityManager;
   
    //all database operations.   
}
The Signup_Roo_Jpa_ActiveRecord aspect will contribute all database operations to the entity class.
Let us explore the ~.web package.
Spring Roo also generated a custom ConversionService for this project.
@RooConversionService
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {

    @Override
    protected void installFormatters(FormatterRegistry registry) {
        super.installFormatters(registry);
        // Register application converters and formatters
    }
}
The ApplicationConversionServiceFactoryBean_Roo_ConversionService aspect includes some conversions, such id(Long) to Object, string to Object, etc which are useful in the page scoped select dropdown controls.
privileged aspect ApplicationConversionServiceFactoryBean_Roo_ConversionService {
    
    declare @type: ApplicationConversionServiceFactoryBean: @Configurable;
    
    @Autowired
    ConferenceService ApplicationConversionServiceFactoryBean.conferenceService;
     
}

There are also some controllers and related aspects in the ~.web package.
privileged aspect ConferenceController_Roo_Controller {
    
    @Autowired
    ConferenceService ConferenceController.conferenceService;
    
    // the controller methods are omitted.
}

@RequestMapping("/conferences")
@Controller
@RooWebScaffold(path = "conferences", formBackingObject = Conference.class)
public class ConferenceController {
}
They are easy to understand, the ConferenceController declares it as a @Controller and set url path. All related request processing are moved to ConferenceController_Roo_Controller.
In the src/test/java Source node, it includes a ~.model package, some test classes are generated for the Conference entity.
The ConferenceDataOnDemand is use for populating testing data for test classes for specified classes. The codes are easy to understand.
privileged aspect ConferenceDataOnDemand_Roo_Configurable {
    
    declare @type: ConferenceDataOnDemand: @Configurable;
    
}
ConferenceDataOnDemand_Roo_Configurable aspect will contribute annotation @Configurable to the class ConferenceDataOnDemand.
privileged aspect ConferenceDataOnDemand_Roo_DataOnDemand {
    
    declare @type: ConferenceDataOnDemand: @Component;
    
    private Random ConferenceDataOnDemand.rnd = new SecureRandom();
    
    private List ConferenceDataOnDemand.data;
    
    @Autowired
    ConferenceService ConferenceDataOnDemand.conferenceService;
    
    @Autowired
    ConferenceRepository ConferenceDataOnDemand.conferenceRepository;
    
 }
ConferenceDataOnDemand_Roo_DataOnDemand aspect includes all operations of generating a new test object.
The ConferenceIntegrationTest_Roo_Configurable, ConferenceIntegrationTest_Roo_IntegrationTest and ConferenceIntegrationTest is very direct and easy to understand, they include the certain test case.

Remove the Roo specific codes...

Some people maybe enjoy their before experience and feel a little incompatible with AspectJ. An new programming model introduced by Spring Roo means there is a learning curve to developers, you have to spend some time to master it. Roo does not force you to follow the Roo-style. You can remove Roo specific codes easily via Spring ToolSuite if you dislike it.
The Spring ToolSuite includes AspectJ Development Tools, which can help you remove the AspectJ specific files and convert the AspectJ files into standard Java class.
  1. Right click the project node, select Refactor->Push In... in the context menu. In the popup Push In Intertype Declaration dialog, it lists all Aspect files, you can click Preview button the preview the processing result one by one, or click OK button to start the conversion.
    The home page
    After it is done, all of aspect specific files are removed, and the content are merged into the related Java files.
    Note: Currently, we have not added any custom codes into the project. You can easily use Spring ToolSuite to recovery the Roo specific features if you like them. Right click the project node, select Refactor->Push Out... in the context menu, it will regenerate the AspectJ files for you. It is magic!? Open the Conference entity class, you will find the before removal did not remove @Roo annotations, it will guide the IDE to restore the AspectJ files.
  2. Remove the Roo specific annotations from all classes.
    If you apply the mutil layered architecture, do not forget to create a new SignupRepository for Signup entity, and move all database operations to SignupRepository, add related business methods in the ConferenceService class, and change the codes of SignupController, and call the business method from ConferenceService instead of Signup.
  3. Remove AJDT and Roo nature from IDE, and remove Roo related dependencies from pom.xml.
Run the project again, mvn tomcat:run. You will find the project works as before.
发表评论

此博客中的热门博文

JPA 2.1: Attribute Converter

JPA 2.1: Attribute Converter If you are using Hibernate, and want a customized type is supported in your Entity class, you could have to write a custom Hibernate Type. JPA 2.1 brings a new feature named attribute converter, which can help you convert your custom class type to JPA supported type. Create an Entity Reuse thePostentity class as example. @Entity @Table(name="POSTS") public class Post implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="ID") private Long id; @Column(name="TITLE") private String title; @Column(name="BODY") private String body; @Temporal(javax.persistence.TemporalType.DATE) @Column(name="CREATED") private Date created; @Column(name="TAGS") private List<String> tags=new ArrayList<>(); } Create an attribute converter In this example…

Auditing with Hibernate Envers

Auditing with Hibernate Envers The approaches provided in JPA lifecyle hook and Spring Data auditing only track the creation and last modification info of an Entity, but all the modification history are not tracked. Hibernate Envers fills the blank table. Since Hibernate 3.5, Envers is part of Hibernate core project. Configuration Configure Hibernate Envers in your project is very simple, just need to addhibernate-enversas project dependency. <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-envers</artifactId> </dependency> Done. No need extra Event listeners configuration as the early version. Basic Usage Hibernate Envers provides a simple@Auditedannotation, you can place it on an Entity class or property of an Entity. @Audited private String description; If@Auditedannotation is placed on a property, this property can be tracked.

Create a restful application with AngularJS and Zend 2 framework

Create a restful application with AngularJS and Zend 2 framework This example application uses AngularJS/Bootstrap as frontend and Zend2 Framework as REST API producer. The backend code This backend code reuses the database scheme and codes of the official Zend Tutorial, and REST API support is also from the Zend community. Getting Started with Zend Framework 2Getting Started with REST and Zend Framework 2 Zend2 provides aAbstractRestfulControllerfor RESR API producing. class AlbumController extends AbstractRestfulController { public function getList() { $results = $this->getAlbumTable()->fetchAll(); $data = array(); foreach ($results as $result) { $data[] = $result; } return new JsonModel(array( 'data' => $data) ); } public function get($id) { $album = $this->getAlbumTable()->getAlbum($id); return new JsonModel(array("data" => $album)); } …