Beehive只是提供了三種訪問企業資源的系統控件現實情況下我們需要訪問更多類型的企業資源所以我們需要自己來開發符合自己需要的控件在這篇文章裡作者將介紹如何基於控件架構開發訪問JavaMail資源的控件簡化對JavaMail資源的訪問
從前面的文章中我們已經學習了Beehive中提供的三種訪問企業資源的控件JDBC控件EJB控件和JMS控件而這也是Beehive中目前已經提供的全部系統控件然而JEE標准中提供的企業資源類型遠不止這三種開發者選擇等待Beehive開發組提供更多的控件顯然是不明智的我們必須自己動手來解決控件的開發
本文中就將給大家介紹如何基於控件架構提供的API來開發自己的控件我們選擇開發者經常需要訪問的企業資源——JavaMail作為目標資源按照控件的命名規則我們暫且叫做JavaMail控件吧
要完成一個控件的設計通常需要完成如下的工作
確定控件要完成的功能
分析要完成控件的功能確定沒有使用控件之前我們通常需要提供哪些參數這些參數是否可能組合成集合使用參數是否必須提供參數的類型等
根據分析結果設計和實現對應於這些參數的注釋這些注釋能夠完整地體現第二步中分析的結果
定義和實現控件公共類
根據控件的使用情況確定控件公共接口類中需要對外提供的接口方法要確定這些接口方法可能會比較困難因為有些時候控件設計者可能難於確定是否為控件使用者提供低級接口以便使用者能夠深入的控制控件的行為
提供控件實現類
控件實現類通常被設計成可擴展(實現orntrolsapibeanExtensible接口)的這樣方便使用者擴展該實現類控件實現類中最主要的方法是invoke(Method mObject[] args)方法這個方法的參數 m 代表控件使用者調用的業務方法而 args 數組則對應著控件使用者調用該業務方法時提供的調用參數控件實現類需要根據使用者提供的注釋和調用業務時提供的參數完成控件主體功能的實現
下面的章節中我們將詳細地講解如果依照上面的步驟來完成JavaMail控件的設計和開發本文中所有例子的源代碼可以在通過資源下載區中的連接完成下載
控件功能定義
JavaMail控件的開發目標是完成訪問SMTP服務器發送郵件的封裝提供足夠的注釋滿足開發者在發送郵件時需要設置的參數同時提供郵件發送的實現方法使開發者在使用控件後無需編寫訪問SMTP服務器設置郵件發送者/接收者等參數發送郵件的代碼而只需要將精力集中到業務邏輯上
需要提供的注釋
在開發控件之前我們需要確定向開發者提供哪些注釋才能夠滿足他們定制控件的實際需求因此我們首先要分析開發者使用這些控件時通常需要提供哪些參數要訪問SMTP服務器發送郵件開發需要提供的參數分為兩類
目標SMTP服務器的參數
目標SMTP服務器的參數包括目標SMTP服務器的地址訪問SMTP服務器是否需要提供安全信息以及訪問SMTP服務器所需要的用戶名和密碼其中目標SMTP服務器的地址是必須的我們需要提供的參數和相關要求如表所示
表 SMTP服務器參數表
參數名 |
參數類型 |
參數說明 |
是否必須提供 |
說明 |
serverAddress
String
SMTP服務器的地址
是
比如SMTP服務的地址是
authorizationRequired
boolean
使用SMTP服務器發送郵件時是否需要提供安全信息
否
如果需要提供安全信息
這個參數設為true
否則設為false
principal
String
訪問SMTP郵件服務器時使用的用戶名
否
credentials
String
訪問SMTP郵件服務器時使用的密碼
否
被發送郵件的參數
要發送一個郵件我們需要提供的參數和相關要求如表所示
表 郵件參數表
參數名 |
參數類型 |
參數說明 |
是否必須提供 |
例子 |
from
String
郵件發送者
是
to
String
郵件的接收者
可以使用
的形式傳遞多個接收者
是
比如我們可以使用
來設置郵件有兩個接收者
cc
String
郵件抄送的接收者
可以使用
的形式傳遞多個抄送的接收者
否
bcc
String
郵件暗送的接收者
可以使用
的形式傳遞多個暗送的接收者
否
subject
String
郵件的主題
否
contentType
String
被發送郵件的格式
默認使用text/plain
另外一種可選的類型是text/html
你還可以在這個參數中加入字符集的設置
否
比如我們可以使用
text/html;charset= GB
來設置被發送的郵件使用html格式
編碼使用GB
attachment
String
郵件附件
否
Content
Object
郵件內容
否
注釋設計實現
從以上兩種參數常見的使用場景來看由於同一個應用中SMTP服務器通常是比較固定的所以SMTP服務器的參數也是比較固定的而被發送郵件的參數則隨著業務的變化而不斷發生變化充分考慮到這種不同點我們需要為上面這兩種種情況提供不同的注釋以及注釋的使用范圍
SMTPServer注釋
SMTP服務器參數的注釋名為SMTPServer它可以設計成一個類級別的注釋用於為控件的繼承類提供注釋根據前面表 的分析SMTPServer必須提供四個參數serverAddressauthorizationRequiredprincipalcredentials
由於和JavaMail控件結合非常緊密SMTPServer注釋被設計成控件接口的內部類清單 的第到行顯示了SMTP注釋的全部代碼代碼中為關鍵的內容提供了相應的提示信息
Message注釋
被發送郵件的參數對應的注釋名為Message由於通常和業務方法密切相關因此我們Message注釋被定義成為控件繼承類中的業務方法提供注釋
根據表 的分析Message對象必須提供個參數分別是fromtoccbccsubjectcontentTypeattachmentContent其中Content(被發送郵件的參數中郵件內容)比較特殊因為他會根據環境的不同而發生變化所以我們將它看作Object類型的變量在注釋中定義Object這種類型沒有必要因此設計時我們不把Content當作的Message注釋的屬性而是將它變成業務方法必須提供的參數
Message注釋被設計成JavaMail控件接口的內部類Message注釋的全部代碼參見清單 的第~行代碼
控件接口
由於郵件發送的功能比較簡單因此我們的控件接口類並不需要過多的提供接口方法唯一設計提供的接口方法名為getMailException它的功能是控件使用者調用時返回郵件發送過程中產生的最後一個違例清單 中提供了控件接口類的全部源代碼
清單 src\org\vivianj\beehive\controls\examples\javamail\
JavaMailControljava
package orgntrolsexamplesjavamail;
import javalangannotationElementType;
import javalangannotationInherited;
import javalangannotationRetention;
import javalangannotationRetentionPolicy;
import javalangannotationTarget;
import orntrolsapibeanAnnotationConstraints;
import orntrolsapibeanAnnotationMemberTypes;
import orntrolsapibeanControlInterface;
import orntrolsapipropertiesPropertySet;
/**
* JavaMailControl 是JavaMail控件的公共接口類
*/
@ControlInterface
public interface JavaMailControl {
/**
* 獲取郵件發送過程中產生的最後一個違例
* @return 返回郵件發送過程中產生的最後一個違例
*/
public Throwable getMailException();
/**
* 類級別的注釋用於注釋JavaMail控件中目標SMTP服務器的
相關信息
*/
@PropertySet(prefix = SMTPServer)
/* 使用@ Inherited注釋表示該注釋可以被自動繼承 */
@Inherited
/* 使用AllowExternalOverride表示定義的接口可以被覆蓋 */
@AnnotationConstraintsAllowExternalOverride
@Retention(RetentionPolicyRUNTIME)
/* 使用TYPE和FIRELD表示該注釋能夠作用於類接口
以及類成員變量 */
@Target( { ElementTypeTYPE ElementTypeFIELD })
public @interface SMTPServer {
/**
* SMTP郵件發送服務器的地址這個屬性是必須設置的
*/
String serverAddress();
/**
* 使用該SMTP服務器發送郵件是否需要安全認證
* true 需要安全認證
* false 不需要安全認證
*/
@AnnotationMemberTypesOptional
boolean authorizationRequired() default false;
/**
* 訪問SMTP郵件服務器時使用的用戶名
*/
@AnnotationMemberTypesOptional
String principal() default ;
/**
* 訪問SMTP郵件服務器時使用的密碼
*/
@AnnotationMemberTypesOptional
String credentials() default ;
}
/**
*
* Message 用於注釋繼承類中的業務方法描述被發送郵件的信息
*/
@PropertySet(prefix = Message)
@Inherited
@AnnotationConstraintsAllowExternalOverride
@Retention(RetentionPolicyRUNTIME)
@Target( { ElementTypeMETHOD })
public @interface Message {
/**
* 郵件的發送者
*/
String from();
/**
* 郵件的接收者可以使用的
形式傳遞多個接收者
*/
String to();
/**
* 郵件抄送的接收者可以使用
的形式傳遞多個抄送的接收者
*/
@AnnotationMemberTypesOptional
String cc() default ;
/**
* 郵件暗送的接收者可以使用
的形式傳遞多個暗送的接收者
*/
@AnnotationMemberTypesOptional
String bcc() default ;
/**
* 郵件的主題
*/
@AnnotationMemberTypesOptional
String subject() default ;
/**
* 被發送郵件的類型默認使用text/plain
*/
@AnnotationMemberTypesOptional
String contentType()
default text/plain;charset=UTF;
/**
* 郵件附件
*/
@AnnotationMemberTypesOptional
String attachment() default ;
}
}
從清單中提供的源代碼中我們注意到SMTPServer接口的authorizationRequiredprincipalcredentials屬性之前使用了@AnnotationMemberTypesOptional注釋應用了@AnnotationMemberTypesOptional注釋屬性表示用戶使用過程中可以不為這些屬性提供內容沒有提供@AnnotationMemberTypesOptional注釋的屬性(比如serverAddress)被視為必須設置的屬性控件使用者使用該注釋時必須提供對應的聲明否則將無法通過編譯
控件實現類
JavaMail控件要完成郵件發送的工作必須要完成獲取目標SMTP服務器參數設置SMTP服務器參數獲取用戶業務方法調用中提供的注釋和參數發送郵件等工作其中獲取目標SMTP服務器參數設置SMTP服務器參數的動作通常只需要一次完成就行了我們設計成在控件初始化的時候完成反映到JavaMail控件的實現類中我們可以將這部分工作在onCreate方法中完成
在控件實現類中最主要的方法就是invoke(Method mObject[] args)這個方法中的兩個參數為我們提供了用於調用業務方法時提供的注釋內容和調用參數郵件發送的工作也被設計成在這個方法中完成
JavaMail控件實現類繼承了JavaMail控件的接口類同時實現了orntrolsapibeanExtensible接口這樣JavaMail控件的使用者就可以直接繼承該控件清單中顯示了JavaMail實現類的完整源代碼如下源代碼中在每一個關鍵點都提供了詳細的說明幫助大家理解JavaMail實現類的實現原理請大家重點關注其中的onCreate和invoke兩個方法
清單 src\org\vivianj\beehive\controls\examples\javamail\
JavaMailControlImpljava
package orgntrolsexamplesjavamail;
import javalangreflectMethod;
import javautilProperties;
import javaxactivationDataHandler;
import javaxactivationFileDataSource;
import javaxmailAddress;
import javaxmailBodyPart;
import javaxmailMultipart;
import javaxmailSession;
import javaxmailTransport;
import javaxmailinternetInternetAddress;
import javaxmailinternetMimeBodyPart;
import javaxmailinternetMimeMessage;
import javaxmailinternetMimeMultipart;
import orntrolsapibeanControlImplementation;
import orntrolsapibeanExtensible;
import orntextContext;
import orntextControlBeanContext;
import orntextResourceContext;
import orntextControlBeanContextLifeCycle;
import orntrolsapieventsEventHandler;
/**
* JavaMailControlImpl 用於封裝訪問SMTP郵件服務器發送郵件的操作
*/
@ControlImplementation
public class JavaMailControlImpl implements JavaMailControl Extensible
javaioSerializable {
static final long serialVersionUID = L;
@Context
ControlBeanContext context;
@Context
ResourceContext resourceContext;
private Throwable lastException;
private Session session;
private Properties props;
private String serverAddress;
private boolean authorizationRequired;
private String principal;
private String credentials;
private MimeMessage mimeMessage;
private Multipart multipart;
@EventHandler(field = context
eventSet = LifeCycleclass eventName = onCreate)
public void onCreate() {
SMTPServer server = (SMTPServer) context
getControlPropertySet(SMTPServerclass);
/* 獲取SMTP服務器的地址 */
serverAddress = serverserverAddress();
/* 目標SMTP服務器發送郵件時是否需要安全認證 */
authorizationRequired =
serverauthorizationRequired();
/* 訪問目標SMTP服務器時的用戶名 */
principal = serverprincipal();
/* 訪問目標SMTP服務器時的密碼 */
credentials = servercredentials();
if (props == null)
props = SystemgetProperties();
/* 初始化SMTP服務器地址 */
propsput(mailsmtphost serverAddress);
/* 初始化訪問目標SMTP服務器時是否需要提供安全信息 */
setAuthorizationRequired(authorizationRequired);
}
/**
* 返回郵件發送過程中的最後一個違例對象
*/
public Throwable getMailException() {
return lastException;
}
/**
* 根據用戶提供的注釋和調用方法時傳入的參數完成郵件發送的
動作
*
* @param m
* 被調用的業務方法
* @param args
* 調用業務方法時傳遞的參數
*/
public Object invoke(Method m Object[] args)
throws Throwable {
Message message = (Message) context
getMethodPropertySet(mMessageclass);
/* 獲取郵件的接收者 */
String to = messageto();
/* 獲取郵件的發送者 */
String from = messagefrom();
/* 獲取郵件抄送的接收者 */
String cc = ();
/* 獲取郵件暗送的接收者 */
String bcc = messagebcc();
/* 獲取郵件格式 */
String contentType = ntentType();
/* 獲取郵件附件 */
String attachment = messageattachment();
/* 獲取郵件主題 */
String subject = messagesubject();
/* 獲取郵件內容 */
Object body = null;
/* 如果業務方法沒有提供發送方法拋出違例信息 */
if (argslength < ) {
body = ;
throw new IllegalArgumentException(
At most one parameter may be defined as the
+ body of the Mail message);
} else
body = args[];
/* 創建郵件對象 */
createMimeMessage();
/* 設置郵件對象的發送者 */
setFrom(from);
/* 設置郵件對象的接收者 */
setTo(to);
/* 設置郵件對象的抄送接收者 */
setCC(cc);
/* 設置郵件對象的暗送接收者 */
setBCC(bcc);
/* 設置郵件主題 */
setSubject(subject);
/* 設置郵件內容和格式 */
setBody(body contentType);
/* 設置郵件附件 */
setAttachment(attachment);
/* 發送郵件 */
sendMail();
return null;
}
/**
* 設置訪問SMTP服務器時是否需要提供
*
* @param need
* 訪問SMTP服務器時是否需要提供安全信息
true 需要 false 不需要
*/
private void setAuthorizationRequired(boolean need) {
if (props == null)
props = SystemgetProperties();
if (need) {
propsput(mailsmtpauth true);
} else {
propsput(mailsmtpauth false);
}
}
/**
* 創建用於發送的郵件對象
*/
private void createMimeMessage() throws Exception {
try {
/* 獲得郵件發送上下文 */
session = SessiongetDefaultInstance(props null);
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
try {
/* 創建郵件發送消息對象 */
mimeMessage = new MimeMessage(session);
/* 創建郵件發送對象 */
multipart = new MimeMultipart();
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 設置被發送郵件的內容
*
* @param body
* 被發送郵件的內容
* @param contentType
* 被發送郵件的類型
*/
public void setBody(Object body String contentType)
throws Exception {
try {
BodyPart bodyPart = new MimeBodyPart();
bodyPartsetContent(body contentType);
multipartaddBodyPart(bodyPart);
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 設置被發送郵件的主題
*
* @param subject
* 被發送郵件的主題
*/
public void setSubject(String subject) throws Exception {
try {
mimeMessagesetSubject(subject);
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 為被發送郵件增加附件
*
* @param attachment
* 附件的文件路徑
*/
public void setAttachment(String attachment)
throws Exception {
try {
BodyPart bp = new MimeBodyPart();
FileDataSource fileds =
new FileDataSource(attachment);
bpsetDataHandler(new DataHandler(fileds));
bpsetFileName(filedsgetName());
multipartaddBodyPart(bp);
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 設置郵件的發送者
*
* @param from
* 郵件的發送者email地址
*/
public void setFrom(String from) throws Exception {
try {
mimeMessagesetFrom(new InternetAddress(from));
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 設置郵件的接收者
*
* @param to
* 郵件的接收者字符串
*/
public void setTo(String to) throws Exception {
try {
mimeMessagesetRecipients(
javaxmailMessageRecipientTypeTO
InternetAddressparse(to));
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 設置郵件抄送的接收者
*
* @param cc
* 郵件抄送的接收者字符串
*/
public void setCC(String cc) throws Exception {
try {
mimeMessagesetRecipients(
javaxmailMessageRecipientTypeCC
(Address[]) InternetAddressparse(cc));
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 設置郵件暗送的接收者
*
* @param bcc
* 郵件暗送接收者字符串
*/
public void setBCC(String bcc) throws Exception {
try {
mimeMessagesetRecipients(
javaxmailMessageRecipientTypeBCC
(Address[]) InternetAddressparse(bcc));
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
/**
* 發送郵件
*
*/
private void sendMail() throws Exception {
try {
/* 設置郵件發送消息對象 */
mimeMessagesetContent(multipart);
/* 保存郵件發送消息對象 */
mimeMessagesaveChanges();
/* 獲取郵件發送上下文 */
Session mailSession =
SessiongetInstance(props null);
/* 根據SMTP服務器對於安全的不同要求連接SMTP服務器 */
Transport transport =
mailSessiongetTransport(smtp);
if (authorizationRequired)
nnect((String) propsget(
mailsmtphost)principal credentials);
else
nnect((String) propsget(
mailsmtphost) );
/* 發送郵件給所有接收者 */
Address[] tos = mimeMessage
getRecipients(javaxmailMessageRecipientTypeTO);
if (tos != null && toslength > ) {
Systemoutprintln(toslength =
+ toslength);
transportsendMessage(mimeMessage tos);
}
/* 發送郵件給所有抄收的接收者 */
Address[] ccs = mimeMessagegetRecipients(
javaxmailMessageRecipientTypeCC);
if (ccs != null && ccslength > ) {
transportsendMessage(mimeMessage ccs);
Systemoutprintln(ccslength =
+ ccslength);
}
/* 發送郵件給所有暗送的接收者 */
Address[] bccs = mimeMessagegetRecipients(
javaxmailMessageRecipientTypeBCC);
if (bccs != null && bccslength > ) {
transportsendMessage(mimeMessage bccs);
Systemoutprintln(bccslength =
+ bccslength);
}
/* 斷開與SMTP服務器的連接 */
transportclose();
} catch (Exception e) {
eprintStackTrace();
lastException = e;
throw e;
}
}
}
經過上面的這些步驟以後JavaMail控件的主體部分就已經全部完成了我們可以將它編譯打包成jar文件在另外的應用中使用它下面的內容將介紹如何使用JavaMail控件完成JavaMail資源訪問
使用JavaMail控件訪問JavaMail資源
JavaMail控件本身並不能直接用於發送郵件控件使用者必須繼承該控件提供對應的注釋後才能夠完成郵件發送的工作本章節中將給大家講解如何繼承JavaMail控件來完成郵件發送的工作
使用JavaMail控件連接到需要提供安全認證的SMTP服務器
網易(NEASE)公司是我個人比較喜歡的提供免費郵箱服務的網絡公司網易(NEASE)公司提供的免費郵箱對應的SMTP服務器()需要提供安全認證才能夠發送郵件
我們將編寫一段代碼調用已經完成的JavaMail控件訪問需要提供安全信息的網易公司的SMTP服務器為了簡化例子我們假設僅僅需要通過這段代碼將郵件發送給唯一的一個接收者不需要暗送或者抄送給其他人要發送的郵件也沒有附件
清單 中顯示了符合要求的一個例子
清單 src\org\vivianj\beehive\controls\examples\
NeaseJavaMailControljava
package orgntrolsexamples;
import orntrolsapibeanControlExtension;
import orgntrolsexamplesjavamailJavaMailControl;
/**
* NeaseJavaMailControl 用於繼承JavaMail控件完成郵件發送的
* 功能使用郵件服務器
* 使用該SMTP服務器需要提供安全信息
*/
@ControlExtension
@JavaMailControlSMTPServer(serverAddress =
authorizationRequired = true principal = principal
credentials = credentials)
public interface NeaseJavaMailControl
extends JavaMailControl {
/**
* 完成發送郵件的功能
* 郵件發送者的郵箱是
* 郵件接收者的郵箱是
* 郵件主題是hello
* 郵件的內容由控件使用者在調用時使用參數body傳入
*
* @param body
* 郵件的內容
*/
@JavaMailControlMessage(from =
to = subject = hello)
public void sendMail(String body);
}
使用Java控件連接到不需要提供安全認證的SMTP服務器
是我創建的唯J族組織負責維護的一個網站它同時能夠為唯J族組織的部分用戶提供SMTP服務它所使用的SMTP服務器()在發送郵件時不需要用戶提供安全認證信息
我們將編寫一段代碼調用已經完成的JavaMail控件連接到不需要提供安全信息的SMTP服務器—後發送郵件為了簡化例子我們假設僅僅需要通過這段代碼將郵件發送給唯一的一個接收者不需要暗送或者抄送給其他人要發送的郵件也沒有附件
清單 中顯示了符合要求的一個例子
清單 src\org\vivianj\beehive\controls\examples\
VivianjJavaMailControljava
package orgntrolsexamples;
import orntrolsapibeanControlExtension;
import orgntrolsexamplesjavamailJavaMailControl;
/**
* VivianjJavaMailControl 用於繼承JavaMail控件
* 實現通過郵件服務器發送郵件的功能
* 使用該SMTP服務器不需要提供安全信息
*/
@ControlExtension
@JavaMailControlSMTPServer(
serverAddress =
authorizationRequired = false principal = username
credentials = password)
public interface VivianjJavaMailControl
extends JavaMailControl {
/**
* 完成發送郵件的功能
* 郵件發送者的郵箱是p
* 郵件接收者的郵箱是
* 郵件主題是hello
* 郵件的內容由控件使用者在調用時使用參數body傳入
*
* @param body
* 郵件的內容
*/
@JavaMailControlMessage(from = p
to = subject = hello)
public void sendMail(String body);
}
結束語
Beehive發布的時候只是提供了三種系統控件JDBC控件EJB控件和JMS控件分別用於訪問JDBC數據源EJB和JMS然而實際應用情況下我們通常需要訪問更多類型的企業資源所以我們必須要根據實際情況編寫符合實際要求的控件
本文中作者選擇JEE中常見的JavaMail資源作為例子詳細地介紹了如何基於控件架構分析設計實現和訪問JavaMail控件的過程讀者可以根據上面的步驟結合自己的實際需求編寫更多的控件簡化企業應用開發
參考資源
Beehive在線資源
下載資源
本文中例子下載地址
JMSControlExampleszip
作者簡介
肖菁
肖菁 是唯J族()創始人
BEA 杭州User Group負責人
自由撰稿人
開源項目BuildFileDesigner(buildfilede)和V
Security(v
secu)創始人
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19433.html