开发者学堂课程【DAO 开发实战业务分析:业务层结构优化 】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/399/detail/5172
业务层结构优化
内容介绍:
一、功能
二、具体内容
三、总结
一、功能
首先必须要清楚一点,在实际的开发过程之中,业务层应该具备有如下几个功能:
· 负责数据库的关闭处理;
· 负责调用多个数据层的方法进行数据操作;
· 负责进行事务控制。
在之前所实现的业务层的操作子类里面,会发现充斥着大量的如下重复机构:
try
{
//
进行数据层控制
return
DAOF
actory
.getInstance(MemberDAOImpl.class).findAll();
} catch (Exception e) {
throw e ;
/
/
异常产生之后交由控制层调用
}
finally
{
/
/
关闭数据库
D
atabase
C
onnection.
close() ;
}
二、具体内容
· 既然所有的业务层都有可能具备同样的功能形式,那么下面就建议对于整体的操作,采用动态代理的设计模式完成。因为在项目里面业务层的接口和实现子类可能有无数多个,不可能使用静态代理为每一个类设置一个代理类。
·在 cn.mldn.oracle.service.proxy 包之中定义一个 ServiceProxy 动态代理类。
但此动态代理类的写法要与反射相结合。动态代理类的前提实践要求是需要实现InvocationHandler接口。动态代理的基本操作要求是需要有一个真实的操作位对象,即 private Object target ; //需要有一个真实的操作位对象。客户端要想取得业务层对象,一定要有业务层的工厂。
当代码改为动态代理模式后,业务层工厂方法应该返回代理类对象,需要跟上public < T > bind() {,而此如果有Class则会产生对象,即 public < T > bind(Class < T > cls) {。返回到Service接口上,其中也存在 cls ,从而代码的最好写法为 return new ServiceProxy().bind(cls) ;,负责产生对象,产生的对象是包含有指定业务层接口真实对象的代理类对象。
动态代理类的实现需要 return Proxy.newProxyInstance(cls.get, interfaces, h),也可直接找到 this.target = cls.newInstance9};,再进行跟上
returnProxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass.getInterfaces(), this),于是返回代理类对象,但里面存在泛型,所以将其强制转成具体的泛型的子类型,加上 try {、} catch(Exception e);做一个处理。
如果出现一个错误,将异常输出,即return null ;,将警告压制下去,这就实现了一个代理对象的动态生成。用泛型的目的是为了能返回指定类型。所以ServiceProxy之中要通过 return new ServiceProxy().bind(cls) ;进行操作。invoke中最真实的处理模式是要返回业务层的操作结果。
业务层的操作结果通过 Object ret = method.invoke(this.target, args) ;,但是在过程之中都会抛异常,如果代码之中产生了异常,要向上抛异常,即 throw e ;,再跟上} finally {,然后再进行跟上 DatabaseConnection.close();,于是就实现了动态代理设计模式的操作。
范例如下:
package
cn.
mldn.oracle.service
.
proxy;
i
mport java.lang.reflect.InvocationHandler;
i
mport java.lang.reflect.Method;
i
mport java.lang.reflect.Proxy;
import
cn.
mldn.oracle.dbc.DatabaseConnection;
p
ublic class ServiceProxy implements InvocationHandler {
private
O
bject
target
;
//
需要有一个真实的操作位对象
@SuppoessWarnings(“unchecked”)
public
<
T
>
bind(
C
lass
<
T
>
cls
)
{
try {
this.target = cls.newInstance();
retur
nP
roxy.
newProxyInstance(
this.
target
.
getClass().getClassLoader(),
this.
target.getClass.getInterfaces(), this);
} catch (Exception e) {
e.printStackTrace() ;
}
return null
;
}
@O
verride
public
O
bject
invoke
(Object proxy, Method method, Object[] args) throws e;
try {
@O
verride
public
O
bject
invoke
(Object proxy, Method method, Object[] args) throws e;
try {
Object
ret
=
method.
invoke(this.target, args) ;
return ret ;
} catch (Exception e
)
{
throw
e
;
}
finally
{
Database
C
onnection.close();
}
}
}
在基础结构之中只是实现了数据库的关闭操作,只是要调用真实的业务层主题,而后实现数据库的关闭处理,最关键的是所有的代理类与业务层都能通过代理类操作进行关闭。
· 那么随后可以将 MessageService 子类里面所有方法中的异常处理和数据库关闭部分取消掉。所以要将 try、catch 全部删掉。代理的设计思想是处理核心业务,所有可能产生重复的处理操作全部取消掉,而这样的模式便实现了代理设计模式的处理过程。再做一次 junit 测试后正常通过。
· 那么此时每一个业务层的实现子类都可以采用动态代理对象,对数据库的关闭进行统一方式的处理。在整个过程之中,客户端测试的操作没有发生任何更改,因为处理过程中 ServiceFactory 进行改变后,将真实主题类对象变成了代理类对象返回。
· 但是现在有一点比较麻烦:在代理设计模式之中还需要考虑到事务的控制问题。
所有的更新处理都需要事务的操作控制。找到业务层接口,以 add、edit、remove等开头的需要进行事务的操作控制, 所以更好的控制指的是对方法的名称的开头做一个限制,例如如果以 add*、edit*、remove*、delete*、update* 等等为开头的方法都应该自动启动事务控制。
此时将 method 取出来,即 String
method
N
ame
=
method.
getName()
;,表示取得方法名称。
而后要在这里进行一个判断,判断要考虑两种情况:一是需要事务;二是不需要事务。
如果if
(method
N
ame.
startsWith(“add”
)
||
method
N
ame.
startsWith(“
edit
”
)
||
method
N
ame.
startsWith(“
remove
”
))
{
,
则按照一种方式处理,其余的则按照另外一种方式处理,即} else {,跟上= method.invoke(this.target.args) ;,无论怎样处理,都需要操控和返回值,这时则需要 Connection 来进行控制。
事务的最原始的控制操作由 Connection 接口定义,所以可以直接利用DstabaseConnection 类中的 getConnection()方法取得Connection接口对象。如果想要操作此流程,则需要跟上 DstabaseConnection. getConnection().setAutoCommit(false); 但要加上子的异常而不是 wand 异常,因为wand异常是控制关闭的。跟上} catch(Exception e) {,如果没有问题,ret
=
method
.invoke(this.target, args) ;,
调用真实主题操作。如果没有问题,则要提交,即
Dstabase
C
onnection. getConnection
().
commit();
,如果有问题则提交Dstabase
C
onnection. getConnection
().
rollback()
;,进行一个回滚。整个代码完成后则要进行关闭。程序执行测试后通过,才是真正意义上可以真正使用的代理类,
如下:
package
cn.
mldn.oracle.service
.
proxy;
i
mport java.lang.reflect.InvocationHandler;
i
mport java.lang.reflect.Method;
i
mport java.lang.reflect.Proxy;
import
cn.
mldn.oracle.dbc.DatabaseConnection;
p
ublic class ServiceProxy implements InvocationHandler {
private
O
bject
target
;
//
需要有一个真实的操作位对象
@SuppoessWarnings(“unchecked”)
public
<
T
>
bind(
C
lass
<
T
>
cls
)
{
try {
this.target = cls.newInstance();
retur
nP
roxy.
newProxyInstance(
this.
target
.
getClass().getClassLoader(),
this.
target.getClass.getInterfaces(), this);
} catch (Exception e) {
e.printStackTrace() ;
}
return null
;
}
@O
verride
public
O
bject
invoke
(Object proxy, Method method, Object[] args) throws e;
try
{
object
ret
=
null;
String
method
N
ame
=
method.
getName() ;
/
/
表示取得方法名称
if
(method
N
ame.
startsWith(“add”
)
||
method
N
ame.
startsWith(“
edit
”
)
||
method
N
ame.
startsWith(“
remove
”
))
{
try {
D
ata
base
C
onnection.getConnection
().setAutoCommit(false);
r
et = method.invoke(this.target, args) ;
D
ata
base
C
onnection.getConnection
().commit() ;
}
catch(Exception
e
) {
D
ata
base
C
onnection.getConnection
().rollback() ;
}
}else{
ret = method.invoke(this.target, args) ;
}
return ret ;
} catch (
Exception
e
) {
throw e ;
} finally {
DatabaseConnection.close() ;
}
}
在这个代理程序中,有着全面的连接控制和事务控制,但本程序的最大缺陷在于所有需要进行事务控制的方法都必须以明确的代码形式进行编写后才可以实现控制。
三、总结
这个时候的代码又被优化了,而此时给出的 DatabaseConnection 以及ServiceProxy 两个类都可以在各个项目使用,前提是项目运用的是原始技术而没有使用开发框架,框架的好处是简化开发。