In the previous section , "The First Battle of SSH Framework Online Mall Project: Integration of Struts2, Hibernate4.3 and Spring4.2", we have built the development environment for Struts2, Hibernate and Spring and successfully integrated them together. This section mainly completes some basic additions, deletion, modification and search, as well as the extraction of Service, Dao and Action.
1. Extraction of Service layer
In the previous section, we simply wrote the save and update methods in the service layer. Here we start to improve the code in this part and then extract the code in the service layer.
1.1 Improve the CategoryService layer
The operation of the database is nothing more than adding, deleting, modifying and checking. First, let’s improve the interface and implementation of the CategoryService layer:
//CategoryService interface public interface CategoryService extends BaseService<Category> { public void save(Category category); //Insert public void update(Category category); //Update public void delete(int id); //Delete public Category get(int id); //Get a Category public List<Category> query(); //Get all Category} Specific implementation of the CategoryService interface:
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { private SessionFactory sessionFactory; //Spring will inject public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } protected Session getSession() { //Get session from the current thread, if not, create a new session return sessionFactory.getCurrentSession(); } @Override public void save(Category category) { getSession().save(category); } @Override public void update(Category category) { getSession().update(category); } @Override public void delete(int id) { /*The first method has a disadvantage, that is, you have to query it once before you delete it. Object obj = getSession().get(Category.class, id); if(obj != null) { getSession().delete(obj); }*/ String hql = "delete Category while id=:id"; getSession().createQuery(hql) // .setInteger("id", id) // .executeUpdate(); } @Override public Category get(int id) { return (Category) getSession().get(Category.class, id); } @Override public List<Category> query() { String hql = "from Category"; return getSession().createQuery(hql).list(); } } 1.2 Service layer extraction implementation
After completing the CategoryService, we will extract the basic implementation of the Service layer. The idea is as follows: We extract a base interface BaseService and the baseServiceImpl, and when developing later, if a new service is needed, you only need to do two steps: first define a new interface xxxService inherits the BaseService interface, which can add new abstract methods to this interface; then define a new implementation class xxxServiceImpl inherits BaseServiceImpl and implements the xxxService interface. This makes it easier to maintain the project.
Let’s first create the BaseService interface based on the above CategoryService interface:
//Basic interface BaseService, use the generic public interface BaseService<T> { public void save(T t); public void update(T t); public void delete(int id); public T get(int id); public List<T> query(); } Then create the BaseService interface implementation class BaseServiceImpl according to the CategoryServiceImpl implementation class:
/** * @Description TODO (extraction of public modules) * @author eson_15 * */ @SuppressWarnings("unchecked") public class BaseServiceImpl<T> implements BaseService<T> { private Class clazz; //The type of the current operation is stored in the clazz, that is, the generic T private SessionFactory sessionFactory; public BaseServiceImpl() { //The following three printing information can be removed. Here is the System.out.println("this represents the object that currently calls the constructor" + this); System.out.println("Get the parent class information of the current this object" + this.getClass().getSuperclass()); System.out.println("Get the parent class information of the current this object (including generic information)" + this.getClass().getGenericSuperclass()); //Get the parameter type of the generic ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); clazz = (Class)type.getActualTypeArguments()[0]; } public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } protected Session getSession() { //Get session from the current thread, if not, create a new session return sessionFactory.getCurrentSession(); } @Override public void save(T t) { getSession().save(t); } @Override public void update(T t) { getSession().update(t); } @Override public void delete(int id) { System.out.println(clazz.getSimpleName()); String hql = "delete " + clazz.getSimpleName() + " as c where c.id=:id"; getSession().createQuery(hql) // .setInteger("id", id) // .executeUpdate(); } @Override public T get(int id) { return (T) getSession().get(clazz, id); } @Override public List<T> query() { String hql = "from " + clazz.getSimpleName(); return getSession().createQuery(hql).list(); } } After the extraction is completed, we can rewrite the CategoryService interface and the CategoryServiceImpl implementation class. as follows:
//CategoryService interface inherits the BaseService interface public interface CategoryService extends BaseService<Category> { /* * Just add the new method required by CategoryService itself. The public method is already in BaseService*/ } /** * @Description TODO (the business logic of the module itself) * @author eson_15 * */ public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService { /* * Just implement the newly added method in the CategoryService interface. The public method has been implemented in BaseServiceImpl */ } As can be seen from the code, the newly added Service only needs to inherit the BaseService interface and then add the business logic required by the Service to the interface. The newly added ServiceImpl only needs to inherit BaseServiceImpl and implement the newly added business logic.
But don't forget the important point: it is to modify the beans in Spring's configuration file beans.xml .
<!-- Generic classes cannot be instantiated, so add lazy-init property--> <bean id="baseService" lazy-init="true"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="categoryService" parent="baseService"/>
Kill the property in the original categoryService, then add the parent property to indicate the inheritance of baseService; then configure the baseService and configure the sessionFactory to the baseService. In addition, one thing to note: set the lazy-init property to true, because baseService is a generic class, and generic classes cannot be instantiated. At this point, the extraction of the Service layer is done.
2. Add an Account in the Service layer
The Service layer has just been extracted, so now it is very simple to write an Account service:
First write an AccountService interface to inherit BaseService:
public interface AccountService extends BaseService<Account> { //Note that the generics in BaseService are now Account /* * Just add the new method required by AccountService itself, and the public method is already in BaseService */ } Then write an AccountServiceImpl implementation class to inherit the BaseServiceImpl implementation class and implement the AccountService interface:
public class AccountServiceImpl extends BaseServiceImpl<Account> implements AccountService { /* * Just implement the newly added methods in the AccountService interface. The public method has been implemented in BaseServiceImpl */ //Manage login function, and it will be improved later} Finally, add the following configuration to the beans.xml file:
<bean id="accountService" parent="baseService" />
In this way, a new service has been written. If you need to add a service in the future, you will follow this process, which is very convenient.
3. Action extraction
3.1 Store data in the action in the domain (request, session, application, etc.)
We know that in Action, you can directly obtain an ActionContext object through ActionContext.getContext(), and then obtain the corresponding domain object through the object; you can also inject the corresponding domain object by implementing the xxxAware interface. Let's first look at these two methods:
public class CategoryAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{ private Category category; private CategoryService categoryService; public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public String update() { System.out.println("----update----"); categoryService.update(category); return "index"; } public String save() { System.out.println("----save----"); return "index"; } public String query() { //Solution 1, use the corresponding map to replace the original built-in object, so that there is no dependency with jsp, but the amount of code is relatively large // ActionContext.getContext().put("categoryList", categoryService.query()); //Put it in the request field// ActionContext.getContext().getSession().put("categoryList", categoryService.query()); //Put it in the session field// ActionContext.getContext().getApplication().put("categoryList", categoryService.query()); //Put it into the application domain//Solution 2, implement the corresponding interface (RequestAware,SessionAware,ApplicationAware), and let the corresponding map inject request.put("categoryList", categoryService.query()); session.put("categoryList", categoryService.query()); application.put("categoryList", categoryService.query()); return "index"; } public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } private Map<String, Object> request; private Map<String, Object> session; private Map<String, Object> application; @Override public void setApplication(Map<String, Object> application) { this.application = application; } @Override public void setSession(Map<String, Object> session) { this.session = session; } @Override public void setRequest(Map<String, Object> request) { this.request = request; } } It is still the CategoryAction class that integrates the three major frameworks in the previous section. We added a query method to this method. In this method, we store the results of the query into the request domain, the session domain and the application domain. The first method is to directly use ActionContext to implement it. No interface is required, but the code is large; the second method implements the RequestAware, SessionAware and ApplicationAware interfaces, and three abstract methods of implementing the interface inject request, session and application, and then assign it to the corresponding member variables, so that the query results can be stored in the domain in the query method. This code volume seems to be larger than the first method... but we can extract it and read it down first.
We add a new query connection to index.jsp to test whether the query results can be displayed:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">Access update</a> <a href="category_save.action">Access save</a> <a href="category_query.action">Query all categories</a><br/> <c:forEach items="${requestScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> <c:forEach items="${sessionScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> <c:forEach items="${applicationScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> </body> </html> 3.2 Extract BaseAction
As mentioned just now, the second method has a larger code volume, but we can extract a BaseAction to specifically handle operations related to these domains.
public class BaseAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware { protected Map<String, Object> request; protected Map<String, Object> session; protected Map<String, Object> application; @Override public void setApplication(Map<String, Object> application) { this.application = application; } @Override public void setSession(Map<String, Object> session) { this.session = session; } @Override public void setRequest(Map<String, Object> request) { this.request = request; } } Then if our own Action needs to use these domain objects to store data, we can directly inherit BaseAction, and we can directly use request, session and application objects. Therefore, the modified CategoryAction is as follows:
public class CategoryAction extends BaseAction { private Category category; <pre name="code"> private CategoryService categoryService; public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public String update() {System.out.println("---update----");categoryService.update(category); return "index"; }public String save() {System.out.println("----save-----");return "index"; } public String query() {request.put("categoryList", categoryService.query()); session.put("categoryList", categoryService.query()); application.put("categoryList", categoryService.query()); return "index"; } public Category getCategory() { return category; } public void setCategory(Category category) {this.category = category; }} All Actions that need to use request, session and application fields are just inherited directly, which is very convenient.
3.3 Get parameters (ModelDriven)
Let's continue to look at the CategoryAction class above. There is a member variable category, which is a POJO. Defining this variable and writing the set and get methods is for the JSP page to be passed in through the parameters attached to the url. The parameters are attributes in the category object, such as id, type, etc., but the parameters in the url must be written as category.id, category.type, etc. In this way, struts will automatically inject this write parameter into the category object, and then we can use this category object directly, but this is a bit cumbersome. We can use ModelDriven to solve the problem more easily.
public class CategoryAction extends BaseAction implements ModelDriven<Category>{ private Category category; //Using the ModelDriven interface, the getModel() method must be implemented. This method will push the returned item to the top of the stack @Override public Category getModel() { category = new Category(); return category; } <pre name="code"> private CategoryService categoryService; public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public String update() { System.out.println("----update----"); categoryService.update(category); return "index"; } public String save() { System.out.println("----save----"); return "index"; } public String query() { request.put("categoryList", categoryService.query()); session.put("categoryList", categoryService.query()); application.put("categoryList", categoryService.query()); return "index"; } } In this way, we don’t need to include the tedious parameters like category.id in the front desk JSP page. Look at the ModelDriven part of the JSP page:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">Access update</a> <a href="category_save.action?id=1&type=haha&hot=true">Test ModelDriven</a> <a href="category_query.action">Query all categories</a><br/> <c:forEach items="${requestScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> <c:forEach items="${sessionScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> <c:forEach items="${applicationScope.categoryList }" var="category"> ${category.id } | ${category.type } | ${category.hot } <br/> </c:forEach> </body> </html> The test result is that catgory can be obtained and all id, type and hot attributes are assigned well. We can see that by implementing the ModelDriven interface, we can easily carry parameters in the url. In Action, we only need to implement the getModel method and return a new object to use. At this point, it is easy to think that there will definitely be many such models in struts that need to be obtained, so we also need to extract this part into BaseAction.
3.4 Extract ModelDriven to BaseAction
First, we add the code of the ModelDriven part to BaseAction, as follows:
//Because there are many different models that require ModelDriven, we use generic public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> { protected Map<String, Object> request; protected Map<String, Object> session; protected Map<String, Object> application; protected T model; @Override public void setApplication(Map<String, Object> application) { this.application = application; } @Override public void setSession(Map<String, Object> session) { this.session = session; } @Override public void setRequest(Map<String, Object> request) { this.request = request; } @Override public T getModel() { //Here, new a corresponding instance by parsing the T passed in. ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass(); Class clazz = (Class)type.getActualTypeArguments()[0]; try { model = (T)clazz.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } return model; } } After the extraction, the code in CategoryAction will decrease and decrease:
//Inherit BaseAction and add generic public class CategoryAction extends BaseAction<Category> { private CategoryService categoryService; public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public String update() { System.out.println("----update----"); categoryService.update(model);//Use model directly return "index"; } public String save() { System.out.println("----save----"); System.out.println(model); //Use model directly to return "index"; } public String query() { request.put("categoryList", categoryService.query()); session.put("categoryList", categoryService.query()); application.put("categoryList", categoryService.query()); return "index"; } } At this point, there is another thing that doesn't feel good about it, which is the member variable categoryService, which has always existed in CategoryAction. Because CategoryAction uses methods in the categoryService object, this object must be created and a set method can be injected. This leads to a disadvantage: if many Actions need to use categoryService, then the object and set method must be created in their Actions. Moreover, if several different service objects are used in an Action, all of them have to be created, which becomes very complicated.
3.5 Extract service to BaseAction
In response to the above problem, we extract all service objects in the project into BaseAction to create it. In this way, after other Actions inherit BaseAction, they can use whatever service they want to use:
//I classified the content in BaseAction public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> { //service object protected CategoryService categoryService; protected AccountService accountService; public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public void setAccountService(AccountService) accountService) { this.accountService = accountService; } //Domain object protected Map<String, Object> request; protected Map<String, Object> session; protected Map<String, Object> application; @Override public void setApplication(Map<String, Object> application) { this.application = application; } @Override public void setSession(Map<String, Object> session) { this.session = session; } @Override public void setRequest(Map<String, Object> request) { this.request = request; } //ModelDriven protected T model; @Override public T getModel() { ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass(); Class clazz = (Class)type.getActualTypeArguments()[0]; try { model = (T)clazz.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } return model; } } This makes the CategoryAction more refreshing: public class CategoryAction extends BaseAction<Category> { public String update() { System.out.println("---update----"); categoryService.update(model); return "index"; } public String save() { System.out.println("---save----"); System.out.println(model); return "index"; } public String query() { request.put("categoryList", categoryService.query()); session.put("categoryList", categoryService.query()); application.put("categoryList", categoryService.query()); return "index"; } } Some people may ask, will it not be redundant if so many service objects are injected into BaseAction? This is not true, because even if it is not written in BaseAction, the Spring container will create this object, which does not matter. On the contrary, the service object is placed in BaseAction and is more convenient for the development of other Actions. Moreover, BaseAction does not need to be assigned to the struts.xml file, because no JSP will request BaseAction, it is just for other Actions to inherit.
Another thing to forget: that is to modify the configuration in beans.xml:
<!-- If it is a prototype type, it is created when used, not automatically when starting--> <bean id="baseAction" scope="prototype"> <property name="categoryService" ref="categoryService"></property> <property name="accountService" ref="accountService"></property> </bean> <bean id="categoryAction" scope="prototype" parent="baseAction"/>
Add a new baseAction bean, match all service objects in the project as property, and kill the property in the original categoryAction.
In the future, if we want to write a new xxxAction, we can directly inherit BaseAction. If a service is used in xxxAction, we can use it directly. We just need to add a bean corresponding to xxxAction in the beans.xml file and configure the jump in the struts.xml file.
4. Change xml to annotation
We can see that as the project gets bigger and bigger, there will be more and more configurations in beans.xml, and many configurations are redundant. In order to make it easier to develop, we now change the configuration of xml into annotations. Let’s first look at the configuration in beans.xml:
These are the beans we wrote when we built the environment and extracted them. These need to be converted into annotations. Let’s replace them piece by piece: first replace the service part, which has three parts: baseService, categoryService and accountService. Replace as follows:
Then kill the corresponding part in beans.xml. Next, modify the ActIon part, mainly baseAction, categoryAction and accountAction, and replace it as follows:
Then kill the configuration of the Action part in beans.xml, and finally add the following configuration to the beans.xml file, and you can use the annotation.
<context:component-scan base-package="cn.it.shop.."/>
Some people may ask, why are service and action different when using annotations? @Service is used in the service and @Controller is used in the action? In fact, it is the same, just to distinguish them from different layers of beans for easy reading.
The source code download address of the entire project: //www.VeVB.COM/article/86099.htm
Original address: http://blog.csdn.net/eson_15/article/details/51297698
The above is the entire content of the second battle of the SSH framework online mall project. I hope it will be helpful to everyone's learning, and I hope everyone will support Wulin.com more.