權(quán)限子系統(tǒng)與文檔處理子系統(tǒng)交織的弊病
如同一個(gè)成規(guī)模的公司一樣,對(duì)于一個(gè)成規(guī)模的應(yīng)用來說,內(nèi)中會(huì)存在著各種子系統(tǒng)以執(zhí)行不同的功能,而且為了協(xié)調(diào)工作,它們往往是交織在一起,就像上一個(gè)版本中權(quán)限子系統(tǒng)與文檔處理子系統(tǒng)交織一樣。
子系統(tǒng)交織是必要的,但子系統(tǒng)間過多過緊密的耦合并不合理。因?yàn)榻豢棔?huì)增加軟件系統(tǒng)的熵值,使得系統(tǒng)隨著功能的增加和細(xì)化變得越來越復(fù)雜而難于控制,最后不得不推倒重來,給個(gè)人和公司帶來巨大的損失。這是軟件業(yè)普遍的問題,在國(guó)內(nèi)公司和外包公司都不罕見。
因此,在程序的編寫過程中,程序員必須有意識(shí)的減少子系統(tǒng)之間的交織,使它們離散化,利用Spring的AOP可以有效幫助我們做到這一點(diǎn)。對(duì)于簡(jiǎn)單值權(quán)限系統(tǒng),使用AOP進(jìn)行子系統(tǒng)離散化的具體思路如下:
1.將DocService中負(fù)責(zé)權(quán)限的代碼(屬性和函數(shù))都分離,放置到一個(gè)專門的權(quán)限處理類PrivilegeService中。
2.使用Spring的ProxyFactoryBean,做成DocService的代理,讓PrivilegeService作為它的前置處理器。
3.在PrivilegeService的before方法中,執(zhí)行權(quán)限檢查,如果權(quán)限值不夠則拋出異常。
分離后DocService簡(jiǎn)潔的代碼
package com.heyang.aopstyle.service;

import com.heyang.traditonal.domain.Doc;
import com.heyang.traditonal.domain.User;
import com.heyang.traditonal.service.IDocService;


/** *//**
* 為領(lǐng)域?qū)ο驞oc提供服務(wù)
*
* @author 何楊(heyang78@gmail.com)
*
* @since 2008-12-30 下午05:13:29
* @version 1.00
*/

public class DocService implements IDocService
{

public void add(Doc doc, User user)
{
System.out.println("將" + doc + "交由dao處理(存入數(shù)據(jù)庫(kù))");
}


public void delete(Doc doc, User user)
{
System.out.println("將" + doc + "交由dao處理(從數(shù)據(jù)庫(kù)刪除)");
}


public void update(Doc doc, User user)
{
System.out.println("將" + doc + "交由dao處理(更新數(shù)據(jù)庫(kù)中對(duì)應(yīng)的記錄)");
}
}
新組建的PrivilegeService類
package com.heyang.aopstyle.service;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

import com.heyang.aopstyle.exception.PrivilegeNotEnoughException;
import com.heyang.traditional2.dao.PrivilegeDao;
import com.heyang.traditonal.domain.User;


/** *//**
* 實(shí)現(xiàn)權(quán)限子系統(tǒng)
* @author: 何楊(heyang78@gmail.com)
* @date: 2009-1-2-下午04:19:13
*/

public class PrivilegeService implements MethodBeforeAdvice
{
private PrivilegeDao privilegeDao;
// 添加doc需要的權(quán)限名
private String addDocPrivilegeName;
// 刪除doc需要的權(quán)限名
private String deleteDocPrivilegeName;
// 更新doc需要的權(quán)限名
private String updateDocPrivilegeName;

/** *//**
* 在IDocService的實(shí)際方法開始前進(jìn)行前置處理--權(quán)限檢查
*/

public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable
{
// 取得方法名
String mothodName=arg0.getName();
// 取得用戶權(quán)限
User user=(User)arg1[1];
int userPrivilegeValue=user.getPrivilegePoint();
// 根據(jù)方法名判斷用戶是否達(dá)到了需要的權(quán)限,否則拋出異常

if("add".equals(mothodName))
{

if(userPrivilegeValue<=getPrivilegeValueBy(addDocPrivilegeName))
{
throw new PrivilegeNotEnoughException("用戶權(quán)限必須達(dá)到"+getPrivilegeValueBy(addDocPrivilegeName)+"才能執(zhí)行添加操作");
}
}

else if("delete".equals(mothodName))
{

if(userPrivilegeValue<=getPrivilegeValueBy(deleteDocPrivilegeName))
{
throw new PrivilegeNotEnoughException("用戶權(quán)限必須達(dá)到"+getPrivilegeValueBy(deleteDocPrivilegeName)+"才能執(zhí)行刪除操作");
}
}

else if("update".equals(mothodName))
{

if(userPrivilegeValue<=getPrivilegeValueBy(updateDocPrivilegeName))
{
throw new PrivilegeNotEnoughException("用戶權(quán)限必須達(dá)到"+getPrivilegeValueBy(updateDocPrivilegeName)+"才能執(zhí)行更新操作");
}
}
}

private int getPrivilegeValueBy(String name)
{
return privilegeDao.getValueBy(name);
}


public PrivilegeDao getPrivilegeDao()
{
return privilegeDao;
}


public void setPrivilegeDao(PrivilegeDao privilegeDao)
{
this.privilegeDao = privilegeDao;
}


public String getAddDocPrivilegeName()
{
return addDocPrivilegeName;
}


public void setAddDocPrivilegeName(String addDocPrivilegeName)
{
this.addDocPrivilegeName = addDocPrivilegeName;
}


public String getDeleteDocPrivilegeName()
{
return deleteDocPrivilegeName;
}


public void setDeleteDocPrivilegeName(String deleteDocPrivilegeName)
{
this.deleteDocPrivilegeName = deleteDocPrivilegeName;
}


public String getUpdateDocPrivilegeName()
{
return updateDocPrivilegeName;
}


public void setUpdateDocPrivilegeName(String updateDocPrivilegeName)
{
this.updateDocPrivilegeName = updateDocPrivilegeName;
}
}
起輔助作用的PrivilegeNotEnoughException類,因?yàn)椴幌敫淖兘涌冢ɑ虿荒芨淖兘涌冢┕拾旬惓n愋投ㄎ贿\(yùn)行期異常:
package com.heyang.aopstyle.exception;


/** *//**
* 權(quán)限不足異常
* @author: 何楊(heyang78@gmail.com)
* @date: 2009-1-2-下午04:02:50
*/

public class PrivilegeNotEnoughException extends RuntimeException
{
private static final long serialVersionUID = -6594794529337011115L;


public PrivilegeNotEnoughException(String msg)
{
super(msg);
}
}
最終配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 數(shù)據(jù)源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="org.gjt.mm.mysql.Driver">
</property>
<property name="url" value="jdbc:mysql://127.0.0.1/test">
</property>
<property name="username" value="root"></property>
<property name="password" value="hy"></property>
</bean>

<!-- 用于訪問數(shù)據(jù)庫(kù)的jdbcTemplate -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<!-- 用于訪問數(shù)據(jù)庫(kù)的PrivilegeTB表,按權(quán)限名取出權(quán)限值的數(shù)據(jù)庫(kù)訪問類 -->
<bean id="privilegeDao"
class="com.heyang.traditional2.dao.PrivilegeDao">
<property name="jdbcTemplate">
<ref bean="jdbcTemplate" />
</property>
</bean>

<!-- 用于文件處理的IDocService實(shí)現(xiàn)類DocService -->
<bean id="docService" class="com.heyang.aopstyle.service.DocService"/>
<!-- 在執(zhí)行docService的實(shí)際方法前進(jìn)行權(quán)限檢查 -->
<bean id="privilegeService" class="com.heyang.aopstyle.service.PrivilegeService">
<property name="privilegeDao">
<ref bean="privilegeDao" />
</property>
<property name="addDocPrivilegeName" value="addDocPrivilege" />
<property name="deleteDocPrivilegeName" value="deleteDocPrivilege" />
<property name="updateDocPrivilegeName" value="updateDocPrivilege" />
</bean>
<!-- docService的代理對(duì)象 -->
<bean id="docServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.heyang.traditonal.service.IDocService</value>
</property>
<property name="interceptorNames">
<list>
<value>privilegeService</value>
</list>
</property>
<property name="target">
<ref bean="docService"/>
</property>
</bean>
</beans>
業(yè)務(wù)處理模擬過程:
package com.heyang.aopstyle;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.heyang.aopstyle.exception.PrivilegeNotEnoughException;
import com.heyang.traditonal.domain.Doc;
import com.heyang.traditonal.domain.User;
import com.heyang.traditonal.service.IDocService;



public class Main
{

public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("aopCtx.xml");
IDocService docService=(IDocService)ctx.getBean("docServiceProxy");
Doc doc=new Doc("論美國(guó)次貸危機(jī)的產(chǎn)生及影響");
User user=new User("中科院經(jīng)濟(jì)所研究員",50);
// 用戶向系統(tǒng)添加文章

try
{
docService.add(doc, user);
}

catch(PrivilegeNotEnoughException ex)
{
System.out.println(ex.getMessage());
}
// 用戶向系統(tǒng)更新文章

try
{
doc.setName("論美國(guó)次貸危機(jī)的產(chǎn)生及影響和我國(guó)應(yīng)該采取的應(yīng)對(duì)措施");
docService.update(doc, user);
}

catch(PrivilegeNotEnoughException ex)
{
System.out.println(ex.getMessage());
}
// 用戶從系統(tǒng)撒刪除文章

try
{
docService.delete(doc, user);
}

catch(PrivilegeNotEnoughException ex)
{
System.out.println(ex.getMessage());
}
}
}

輸出:
2009-01-02 16:26:53,406 DEBUG [main] (DriverManagerDataSource.java:289) - Creating new JDBC Connection to [jdbc:mysql://127.0.0.1/test]
2009-01-02 16:26:56,578 DEBUG [main] (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource
將文件 名=論美國(guó)次貸危機(jī)的產(chǎn)生及影響交由dao處理(存入數(shù)據(jù)庫(kù))
2009-01-02 16:26:56,593 DEBUG [main] (JdbcTemplate.java:382) - Executing SQL query [select value from PrivilegeTB where name='updateDocPrivilege' ]
2009-01-02 16:26:56,593 DEBUG [main] (DataSourceUtils.java:112) - Fetching JDBC Connection from DataSource
2009-01-02 16:26:56,593 DEBUG [main] (DriverManagerDataSource.java:289) - Creating new JDBC Connection to [jdbc:mysql://127.0.0.1/test]
2009-01-02 16:26:56,640 DEBUG [main] (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource
將文件 名=論美國(guó)次貸危機(jī)的產(chǎn)生及影響和我國(guó)應(yīng)該采取的應(yīng)對(duì)措施交由dao處理(更新數(shù)據(jù)庫(kù)中對(duì)應(yīng)的記錄)
2009-01-02 16:26:56,640 DEBUG [main] (JdbcTemplate.java:382) - Executing SQL query [select value from PrivilegeTB where name='deleteDocPrivilege' ]
2009-01-02 16:26:56,640 DEBUG [main] (DataSourceUtils.java:112) - Fetching JDBC Connection from DataSource
2009-01-02 16:26:56,640 DEBUG [main] (DriverManagerDataSource.java:289) - Creating new JDBC Connection to [jdbc:mysql://127.0.0.1/test]
2009-01-02 16:26:56,703 DEBUG [main] (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource
2009-01-02 16:26:56,859 DEBUG [main] (JdbcTemplate.java:382) - Executing SQL query [select value from PrivilegeTB where name='deleteDocPrivilege' ]
2009-01-02 16:26:56,859 DEBUG [main] (DataSourceUtils.java:112) - Fetching JDBC Connection from DataSource
2009-01-02 16:26:56,859 DEBUG [main] (DriverManagerDataSource.java:289) - Creating new JDBC Connection to [jdbc:mysql://127.0.0.1/test]
2009-01-02 16:26:56,953 DEBUG [main] (DataSourceUtils.java:312) - Returning JDBC Connection to DataSource
用戶權(quán)限必須達(dá)到60才能執(zhí)行刪除操作

例程下載:
http://m.tkk7.com/Files/heyang/AOPPrivilegeSample20090102170039.rar
需要自行載入的包為:
commons-logging-1.0.4.jar,log4j-1.2.14.jar,spring.jar,mysql-connector-java-5.0.6-bin.jar