SSH项目实战OA-dubbo

Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架,本质上是个远程服务调用的分布式框架.由于我们子系统中web工程和service工程是发布在不同的Tomcat,所以需要使用Dubbo来发布和引用服务.

安装Zookeeper

Dubbo一般会使用Zookeeper作为注册中心,所以我们需要在linux虚拟机中安装Zookeeper服务.

安装方法可以参考我的一篇博客:

Ubuntu下安装zookeeper

 

Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。

如果不用Dubbo,单一工程中spring的配置可能如下:

<bean id="xxxService" class="com.xxx.XxxServiceImpl" />

<bean id="xxxAction" class="com.xxx.XxxAction">

    <property name="xxxService" ref="xxxService" />

</bean>

一旦用了Dubbo,在本地服务的基础上,只需做简单配置,即可完成远程化服务。例如,将上面的配置文件拆分成两份,将服务定义部分放在服务提供方remote-provider.xml,将服务引用部分放在服务消费方remote-consumer.xml,并在提供方增加暴露服务配置<dubbo:service>,在消费方增加引用服务配置<dubbo:reference>。所以,我们要是使用了Dubbo的话,就要分为发布服务和调用服务两部分了,发布服务方式如下:

<!-- 和本地服务一样实现远程服务 -->

<bean id="xxxService" class="com.xxx.XxxServiceImpl" />

<!-- 增加暴露远程服务配置,interface:指定服务的接口,ref:指定接口的实现类 -->

<dubbo:service interface="com.xxx.XxxService" ref="xxxService" />

 

(表现层)调用服务的方式如下:

<!-- 增加引用远程服务配置,interface:指定服务的接口-->

<dubbo:reference id="xxxService" interface="com.xxx.XxxService" />

<!-- 和本地服务一样使用远程服务 -->

<bean id="xxxAction" class="com.xxx.XxxAction">

    <property name="xxxService" ref="xxxService" />

</bean>

 

为了更好说明dubbo的使用方法,让我们能先构建出一个完整的功能,如根据Id查询用户信息,实现service,dao层和controller

构建service层和dao

首先在OA-system-interface中创建包com.QEcode.OA.service,并在该包下,新建一个接口UserService

 

然后我们在OA-system-servicecom.QEcode.OA.service.impl包下新建一个实现类,如下图所示。

写完发现,我们还没创建UserDao,所以还要在OA-system-daosrc/main/java目录下新建包com.QEcode.OA.dao.impl,并在dao包下新建接口UserDao,impl包下新建实现类UserDaoImpl

此外,由于我们需要使用注解来将Dao的实现类注入到spring容器中,以及spring帮我们封装好的hibernate session对象HibernateTemplate,所以需要添加spring的依赖.

那么为什么要使用HibernateTemplate?

我们使用HibernateTemplate,有一个很重要的原因就在于我们不想直接控制事务,不想直接去获取,打开Session,开始一个事务,处理异常,提交一个事务,最后关闭一个Session.HibernateTemplate Hibernate操作进行封装,我们只要简单的条用HibernateTemplate 对象,传入hql和参数,就获得查询接口,至于事务的开启,关闭,都交给HibernateTemplate  对象来处理我们自己只专注于业务,不想去作这些重复而繁琐的操作。

 <!-- Spring -->

       <dependency>

           <groupId>org.springframework</groupId>

           <artifactId>spring-context</artifactId>

       </dependency>

 <dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

</dependency>

 

现在OA.system.dao工程的pom.xml文件如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.QEcode</groupId>
    <artifactId>OA-system</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>OA-system-dao</artifactId>
  <dependencies>
  	<dependency>
  		 <groupId>com.QEcode</groupId>
    	<artifactId>OA-system-pojo</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
  	</dependency>
  	<!-- hibernate -->
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-core</artifactId>
	</dependency>
  	<!-- MySql -->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <!-- 连接池 -->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
      </dependency>
      <!-- Spring -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
       </dependency>
       <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
		</dependency>
      
      
  </dependencies>
  
</project>


现在来写我们的持久层userDao-------等等,先不要急着写userDao.让我们思考一个问题?每一个Dao都需要有增删改查的操作,而这些操作大致上是相同,那么我们需要在每个Dao类中都重复写上同样的代码吗?显然,这是不符合可重用性原则的,我们可以把这些功能一样的代码抽取出来,构成一个BaseDao类,然后让每一个Dao实现类都继承BaseDao,那么我们就不用每次都写增删改查了.

下面让我们来新建这个BaseDao.

dao包下新建接口BaseDao,并在impl包下新建BaseDaoImpl

由于BaseDao是一个抽取dao基本功能的接口,有着增删改查等方法,但是这有个问题,那就是hibernate的方法大多数都需要传入一个实体类的Class对象,并且当我们查询到结果时,需要返回一个实体类对象.

比如根据id查找user的方法:User getById(Long userId),需要返回一个User对象,可是,BaseDao是被所有的Dao所实现,其他Dao需要返回的就不是User对象,而是它自己的实体类对象,所以,我们不能在BaseDao中限定实体类的类型.那么要怎么办呢?可能有人会想到了,那就是泛型<T>.BaseDao,所有实体类型都用泛型来表示,只有当BaseDao被实现时,由实现类根据其本身的类型来指定T的具体类型.

BaseDao具体实现如下:

public interface BaseDao<T> {
   
    /**
     * @Description:保存实体
     * @param entity
     */
    public void save(T entity);
   
    /**
     * @Description:修改实体
     * @param entity
     */
    public void update(T entity);
   
    /**
     * @Description:根据id删除实体
     * @param id
     */
    public void delete(Long id);
   
   
    /**
     * @Description:根据id查询实体
     * @param id 实体id
     * @return
     */
    public T findById(long id);
   
   
    /**
     * @Description:根据多个id获取多个实体
     * @param ids
     * @return
     */
    public List<T> findByIds(Long[] ids);
   
    /**
     * @Description:查询实体列表
     * @return
     */
    public List<T> findAll();
   
 }


下面是BaseDaoImpl的内容:现在我们已经写出了接口,那么就要实现BaseDaoImpl,它实现了BaseDao接口。

public interface BaseDao<T> {
   
    /**
     * @Description:保存实体
     * @param entity
     */
    public void save(T entity);
   
    /**
     * @Description:修改实体
     * @param entity
     */
    public void update(T entity);
   
    /**
     * @Description:根据id删除实体
     * @param id
     */
    public void delete(Long id);
   
   
    /**
     * @Description:根据id查询实体
     * @param id 实体id
     * @return
     */
    public T findById(long id);
   
   
    /**
     * @Description:根据多个id获取多个实体
     * @param ids
     * @return
     */
    public List<T> findByIds(Long[] ids);
   
    /**
     * @Description:查询实体列表
     * @return
     */
    public List<T> findAll();
   
 }

现在我们已经实现了BaseDaoImpl...........并没有,细心一点就会发现Class<T> clazz是一个空值,我们需要给它赋值.那么clazz是什么呢,它是继承了BaseDaoImpl类的dao中实体类的Class对象,UserDaoImpl继承了BaseDaoImpl,那么clazz=User.Class.而想要获取到Class对象,就要设及到反射了.

如果不了解反射,可以参考我的一篇博客:

Java基础-反射

我们可以在创建BaseDaoImpl的时候,初始化clazz对象

public BaseDaoImpl(){
    //利用反射得到T的类型
    ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
    this.clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0];
}

getGenericSuperclass方法的作用是返回直接继承的父类(包含泛型参数

通过下面的一个例子来了解.getGenericSuperclass()

package cn.test;
public class Test{
}
class Person<T> {
}
class Student extends Person<Test> {
}

 

有两个类PersonStudent,并且Student继承了Person,那么Student.class.getGenericSuperclass()返回的是cn.test.Person<cn.test.Test>

完整的BaseDaoImpl类如下:

public class BaseDaoImpl<T> implements BaseDao<T> {
    @Resource(name="hibernateTemplate")
    protected HibernateTemplate hibernateTemplate;
    
    private Class<T> clazz;
    
    
    public BaseDaoImpl(){
	//利用反射得到T的类型
	ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
	this.clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }
    
    /**
     * @Description:保存实体
     * @param entity
     */
    public void save(T entity){
	hibernateTemplate.save(entity);
    }
    
    /**
     * @Description:修改实体
     * @param entity
     */
    public void update(T entity){
	hibernateTemplate.update(entity);
    }

    /**
     * @Description:根据id删除实体
     * @param id
     */
    public void delete(Long id){
	Object object = hibernateTemplate.get(clazz, id);
	hibernateTemplate.delete(object);
    }
    
    
    /**
     * @Description:根据id查询实体
     * @param id 实体id
     * @return
     */
    public T findById(long id){

	return hibernateTemplate.get(clazz , id);

    }
    
    /**
     * @Description:根据多个id获取多个实体
     * @param ids
     * @return
     */
    public List<T> findByIds(Long[] ids){
	//防止空指针异常
	if (ids == null || ids.length ==0) {
	    //返回一个空的集合
	    return Collections.EMPTY_LIST;
	}
	
	List<T> list = new ArrayList<T>();
	for (Long id : ids) {
	    list.add(hibernateTemplate.get(clazz, id));
	}
	return list;
    }
    /**
     * @Description:查询实体列表
     * @return
     */
    public List<T> findAll(){
	return (List<T>) hibernateTemplate.find("from "+clazz.getSimpleName());
    }
}

抽取出BaseDaoBaseDaoImpl,让我们来修改一下我们原来的Dao

 

 

,我们的UserDao已经改造好了,别忘了将UserDaoImpl注入到spring容器中,并且当我们要创建其他Dao类的时候,也要和UserDao一样继承BaseDao.

对了,我们在Dao层使用了注解来将dao注入到spring容器中,那么我们必须要让spring容器启动时扫描dao,而我们之前是没有配置的,所以我们要在OA-system-service下的applicationContext-dao.xml中添加包扫描

 <!-- 配置spring容器创建时要扫描的包 -->

    <context:component-scan base-package="com.QEcode.OA.dao"></context:component-scan>  

现在applicationContext-dao.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        ">
     <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:resource/*.properties"/>    
     <!-- 配置spring容器创建时要扫描的包 -->
    <context:component-scan base-package="com.QEcode.OA.dao"></context:component-scan>   
     <!-- 配置hibernateTemplate,用于持久层 -->
   <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
   		<property name="sessionFactory" ref="sessionFactory"></property>
   </bean>
   <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
   		<property name="dataSource" ref="dataSource"/>
		<!-- hibernate参数设置 -->
		<property name="hibernateProperties">
			<props>
				<!-- 数据库方言 -->
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
				<!-- 显示sql语句-->
				<prop key="hibernate.show_sql">true</prop>
				<!-- 格式化SQL语句 -->
				<prop key="hibernate.format_sql">true</prop>
				<!-- create:根据映射关系生成表 -->
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<prop key="current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop>
			</props>
		</property>
   		 <!-- 使用注解后,用该方式指定实体类的包 -->
   		 <property name="packagesToScan">
   		 	<array>
   		 		<value>com.QEcode.OA.pojo</value>
   		 	</array>
   		 </property>
   </bean>
	 <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="maxActive" value="${jdbc.maxActive}" />
        <property name="minIdle" value="${jdbc.minIdle}" />
    </bean>   
</beans>


接下来,让我们回到service层,修改一下方法名为findById

 

不过,既然在Dao层我们知道了要将一些重复的代码抽取出来,形成一个BaseDao,那么在service,我们也要学会把重复的代码抽取出来,不过service层有点不同,那就是每个service的功能是一样的,但是具体实现是不同的,所以我们只能将接口抽取出来,而不能把实现类也抽取出来.由于service接口是放在OA-system-interface,所以在OA-system-interface下新建一个接口BaseService,并且让UserService继承BaseService

我们将Service层常用的方法抽取出来,放到BaseService

public interface BaseService <T> {
    /**
     * @Description:查询所有
     * @return
     */
    public List<T> findAll();
    
    /**
     * @Description:删除
     * @param role
     */
    public void delete(Long id);

    /**
     * @Description:增加
     * @param role
     */
    public void save(T entity);

    /**
     * @Description:根据id查询
     * @param roleId
     * @return
     */
    public T findById(Long id);

    /**
     * @Description:更新
     * @param role
     */
    public void update(T entity);

    /**
     * @Description:根据id数组查询
     * @param roleIds
     * @return
     */
    public List<T> findByIds(Long[] entityIds);
}


 

不要忘了在UserServiceImpl中重写BaseService的方法.

现在我们只需要实现findById方法,其他方法等需要的时候再写.还要,要记得将UserServiceImpl注入到spring容器中

 

构建controller

OA-system-web工程中的com.QEcode.OA.controller包下创建UserAction.

UserAction内容如下:

@Controller
public class UserAction {
    @Autowired
    private UserService userService;
    
    private User user;

    public String findById(){
	user = userService.findById(user.getUserId());
	return "success";
    }
    
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
}

此外还要在struts.xml中配置action

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
	<!-- 配置Struts2常量 -->
	<!-- 禁用动态方法调用 -->
    <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
    <!-- 开启开发模式,只在项目开发阶段配置 -->
    <constant name="struts.devMode" value="true" />
    <!-- 配置访问后缀为action -->
    <constant name="struts.action.extension" value="action"/>
    <!-- 把主题配置成simple -->
	<constant name="struts.ui.theme" value="simple" />
	
	<!-- 用户 UserAction -->
	<package name="userAction" extends="struts-default" namespace="/user">
		<action name="userAction_*" class="userAction" method="{1}">
			<result name="{1}">/WEB-INF/jsp/userAction/{1}.jsp</result>
			<result name="success">/WEB-INF/success.jsp</result>
		</action>
	</package>
	
	
	
	
	
</struts>

我们已经构建了一个完整的功能,不过现在我们是无法使用的,因为wen工程和service工程是运行在不同的Tomcat中,web工程是无法直接调用service中的方法的.要想成功运行,必须使用dubbo来发布和引用服务

 

 

===============================================================================================

在写博客的时候,可能在项目中有一些问题没有被发现,在我修改后,忘记写到博客上,所以我将这个项目上传到github上,大家可以在github上获取项目的代码

下面是github地址,大家Fork and Star

OA-Reconsitution
 

 

 

 

猜你喜欢

转载自blog.csdn.net/QEcode/article/details/84493131