Springメール送信(非同期+同期)


感謝:http://r164.blog.163.com/blog/static/19749695201043084930451/
プロジェクトでメールを送りましたが、添付ファイルが大きいため、遅く送りました.
ユーザー体験は確かにいいです.
 
Springフレームを用いてパッケージ化されたJavaMailの現実的な同期または非同期メールを送信する.
ネットワークコレクション 2010-05-30 08:49:30 60を読む コメント0  大中小 購読する
転載してから http://howsun.blog.sohu.com/129043957.html
作者:张纪豪
J 2 EEは簡単にJDKに様々なアプリケーションの標準仕様を広げています.メール処理はその中の一つの重要な応用です.それが規範である以上、JDKを通じてメールプロトコルに従ってメール処理システムを作成することができますが、実際には多くのメーカーとオープンソース組織がこのようにしています.ApacheはJ 2 EEの最も積極的な実現者の一つです.もちろん私達のボスであるSUNもいます.
    親分の話をし始めて、感慨無量です!彼はすでにOracleに加入しています.甲骨文(亀の殻に刻まれた文字ではないですか?私の中華です.人類で一番早い言語です.Javaより何千年も前です.)の家元のラリー?エリソンさんはいい水夫です.それはヨットの上にいるとは思わないでください.理由があります.Javaの世界はまだ素晴らしい歴史があると信じています.GoogleのAndroidとChrome OSの二大操作システムは、Javaを優先してアプリケーション開発の基礎言語として、自分の簡単な言語を紹介します.同時に、ChromeOSの見通しは計り知れないものであることを予感した.これはクラウドの計算を推進する重要な構成部分であるかもしれない.Andtrod(主にモバイル機器に使用され、将来は携帯電話で主流となるオペレーティングシステムであるかもしれない)、ひいてはマイクロソフトのWindowsも将来はこのシステムの小さなウィンドウであるかもしれない.マイクロソフトはすでに老朽化しています.大勢のヤフーと協力して、前進のペースは大幅に緩められます.投資家はこの時長くGoogle株を買うことができます.筆者もgoogle検索エンジン、gmailをよく使います.
    じゃ、雑談は少なくして、話はテーマにします.
    みんなが笔者のように一番多く使っているのはヘッドのjavamailかもしれません.メールの机能を実现しましたが、呼び出すにはまだより复雑なコードが必要です.そして、初心者呼出の成功率が低いです.しかし、このような例が多いので、ここではこれらの例示的なコードを繰り返さず、Springフレームにパッケージされたメール処理機能を重点的に利用している.
    工事を始める前に、まず環境を調べます.筆者はwebプロジェクトを開いていますが、必要な基礎構成は以下の通りです.
▲JDK 1.6
▲J 2 EE 1.5 
 
▲JavaMail 1.4は少し説明します.J 2 EE 1.5はメール仕様に組み込まれていますので、開発期間にjavamailのjarパッケージを導入しないでください.運行期間は必要です.だから、jarパッケージをウェブ容器のjavaライブラリに入れてもいいです.文章の終わりはさらに説明します.
▲Spring 2.5
 
▲メールボックス  以上のように、GoogleのGmailを愛用しています.
 
    主要文書リスト:
■Mail Service.java      メール処理対象インターフェース
■Mail ServiceImpl.java  上記実施
■Email.java                  普通のJavaBeanは、メールデータをパッケージ化し、htmlページのフォームに対応しています.
■Mail Controller.java         アクションプロセッサ
■spring-core-config.xml      Springコア配置
    筆者のwebプロジェクトでは、WEB層はSpring@MVCを使用しています.もちろん、その原理を知るだけで、servletやstrutsフレームを利用してweb層の動作プロセッサを行うことができます.他のファイル、例えばweb.xmlは、WEB層の構成を省略します.
 以下のコードを開始します.
spring-core-config.xml:
 <!--①     -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="protocol" value="smtp"/>
<property name="host" value="smtp.gmail.com"/>
<property name="port" value="465" /><!--Gmail SMTP , google -->
<property name="username" value=" google gmail "/>
<property name="password" value=" "/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
                        <!--gmail ssl -->
</props>
</property>
</bean>

<!--② -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10"/>
<property name="maxPoolSize" value="30"/>
</bean>
    これはメール処理の二つのコア構成であり、第一の構成(①)はJavaMail Sender Beanを容器に組み立てることであり、それはJavaMailのパッケージであり、その中で最も重要なのは組み立てプロセスの属性パラメータであり、これらの属性はJavaMail規格に厳格に従うと同時に、メールプロバイダの要求を満たすことであり、例えばSMTPサーバポートはいくらであり、送信時に身分認証が必要か、サーバーがセキュリティ接続、接続時に暗号化されているかどうか、およびどのような暗号化方式を採用しているかは、メールプロバイダが提供しているこれらのパラメータが上記の構成に直接影響を及ぼしています.これは初心者の最も軽視しやすい一環ですので、設定前に必ずメールプロバイダのサイトでメールの技術パラメータを詳細に把握してください.
    同期同期同期送信問題:JavaMailメール処理は同期であり、即ちユーザトリガイベント、SMTP Serverと通信、サーバ復帰状態メッセージ、プログラム終了はシングルパス内であり、この場合、ソケット通信、サーバトラフィック処理速度などの理由で処理時間間に未知数であることが多い.簡単なアプリケーションの例を挙げると、ユーザーが登録を提出しながらアカウントをアクティブにするメールを送信すると、メールサーバのブロックが長い間反応していないために登録が失敗して諦めたということをユーザーは知らないかもしれません.非同期方式は、メール処理タスクを別のスレッドに渡すことで、J 2 EEには2つの解決策があり、1つはJMSを利用して、JMSが同期と非同期のメッセージ処理を実現し、メールを非同期のメッセージとして送信することができる.JMSはJ 2 EEの高級アプリケーションに属しているので、WEB機能のみの容器についてはこのサービスをサポートしていません.例えば、Tomcat(もちろんプラグインを見つけて解決してもいいです.)は、紙幅の制限のため、ここでは新たなモジュールに関連しないです.もう一つの案はJDKのExectorのサポートを利用して、JDK 5.0の後継バージョンはjava.util.co ncurrentの強力な合併ツールパッケージを追加しました.これはアクチュエータ、タイマー、ロック、スレッドセキュリティキュー、スレッドタスクフレームなどを含んでいます.Exector-アクチュエータは、タスクの「提出」と「実行」を分離して結合することができます.私たちのメール処理タスクは、それを借りて非同期的に実行することができます.Springフレームはパッケージを提供しています.②を参照してください.これをどう使うかを見てみます.コードは以下の通りです.
Mail ServiceImpl.java:
package com.zhangjihao.service.impl;

import java.io.IOException;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.task.TaskExecutor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.zhangjihao.bean.Email;
import com.zhangjihao.service.MailService;
import com.zhangjihao.util.StringUtil;

/**
 * :<br>
 *
 * @author
 * @version
 * Build Time Jul 24, 2009
 */
@Service("mailService")
public class MailServiceImpl implements MailService {

 @Resource JavaMailSender mailSender;// Spring javamail,Spring xml
 @Resource TaskExecutor taskExecutor;// Spring
 
 private Log log = LogFactory.getLog(getClass());
 private StringBuffer message = new StringBuffer();
 
 public void sendMail(Email email) throws MessagingException, IOException {
  if(email.getAddress() == null || email.getAddress().length == 0){
   this.message.append(" ");
   return;
  }
  if(email.getAddress().length > 5){// 5 ,
   sendMailByAsynchronousMode(email);
   this.message.append(" , ...<br/>");
  }else{
   sendMailBySynchronizationMode(email);
   this.message.append(" ...<br/>");
  }
 }
 
 /**
  *
  * @see com.zhangjihao.service.MailService#sendMailByAsynchronousMode(com.zhangjihao.bean.Email)
  */
 public void sendMailByAsynchronousMode(final Email email){
  taskExecutor.execute(new Runnable(){
   public void run(){
    try {
     sendMailBySynchronizationMode(email);
    } catch (Exception e) {
     log.info(e);
    }
   }
  });
 }
 
 /**
  *
  * @throws IOException
  * @see com.zhangjihao.service.MailServiceMode#sendMail(com.zhangjihao.bean.Email)
  */
 public void sendMailBySynchronizationMode(Email email) throws MessagingException, IOException {
  MimeMessage mime = mailSender.createMimeMessage();
  MimeMessageHelper helper = new MimeMessageHelper(mime, true, "utf-8");
  helper.setFrom("[email protected]");//
  helper.setTo(email.getAddress());//
  helper.setBcc("[email protected]");//
  if(StringUtil.hasLength(email.getCc())){
   String cc[] = email.getCc().split(";");
   helper.setCc(cc);//
  }
  helper.setReplyTo("[email protected]");//
  helper.setSubject(email.getSubject());//
  helper.setText(email.getContent(), true);//true html
  
  // , , , URL .
  //helper.addInline("logo", new ClassPathResource("logo.gif"));
  
  //
  for(MultipartFile file : email.getAttachment()){
   if(file == null || file.isEmpty()){
    continue;
   }
   String fileName = file.getOriginalFilename();
   try {
    fileName = new String(fileName.getBytes("utf-8"),"ISO-8859-1");
   } catch (Exception e) {}
   helper.addAttachment(fileName, new ByteArrayResource(file.getBytes()));
  }
  mailSender.send(mime);
 }

 public StringBuffer getMessage() {
  return message;
 }

 public void setMessage(StringBuffer message) {
  this.message = message;
 }
}

    このようなMailServiceインターフェースは、3つの方法(インターフェースファイルコードの省略)のみで実現されている.一つの送信トランスポート、一つの同期送信方法、一つの非同期送信方法.実際には、現マールServiceImplのコードにより、メール送信は同期送信という方法だけで、非同期実行が必要な場合は、taskExector非同期実行器に入れるだけの簡単さが分かります.この3つの方法は全部publicで修飾されていますので、上の方ではどれを使ってもいいです.以下は簡単なコールコードを参照してください.
    呼び出し前に、初心者がよりよく受け入れるようにEmail.javaコードを先に書きます.
Email.java:

package com.zhangjihao.bean;

import java.io.Serializable;

import org.springframework.web.multipart.MultipartFile;

import com.zhangjihao.util.StringUtil;

/**
 * :<br>
 *
 * @author
 * @version
 * Build Time Jul 24, 2009
 */
public class Email implements Serializable {

 private static final long serialVersionUID = 9063903350324510652L;
 
 /** : **/
 private UserGroups userGroups;

 /** **/
 private String addressee;
 
 /** **/
 private String cc;
 
 /** **/
 private String subject;
 
 /** **/
 private String content;
 
 /** **/
 private MultipartFile[] attachment = new MultipartFile[0];
 
 ////////////////////////// //////////////////////////////
 
 public String[] getAddress() {
  if(!StringUtil.hasLength(this.addressee)) {
   return null;
  }
  addressee = addressee.trim();
  addressee.replaceAll(";", ";");
  addressee.replaceAll(" ", ";");
  addressee.replaceAll(",", ";");
  addressee.replaceAll(",", ";");
  addressee.replaceAll("|", ";");
  return addressee.split(";");
 }

 /////////////////////////////Getter && Setter///////////////////////////////

 ...... 
}

    このクラスは簡単なJavaBeanで、メールのデータをカプセル化するために、Strutsフレームワークを習慣的に使う読者に対して、アクション・フォームとして完全に理解できます.しかし、MultiiprtFileタイプであり、配列のatachment属性は理解しにくいかもしれません.Strutsフレームワークに詳しいのはFormFileと見られます.Struts 2では分かりやすいかもしれません.筆者はSpring MVCを使っていますが、フレームにはこのような属性エディタが内蔵されていますので、フォームにアップロードされたファイルをこのフィールドに簡単に変換します.
    私達はWEB層の呼び出しを見にきましたが、これまでに本文のテーマを完成しました.ですから、WEBはどのように呼び出してもMailServiceの三つの方法をめぐっています.全面的な認識を持つために、コードをリストしますが、Spring@MVCのいくつかの知識を知る必要があります.
Mail Controller.java:

package com.zhangjihao.web.controller.system;

import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.zhangjihao.bean.Email;
import com.zhangjihao.domain.user.User;
import com.zhangjihao.service.MailService;
import com.zhangjihao.service.UserService;
import com.zhangjihao.util.StringUtil;
import com.zhangjihao.web.controller.MasterController;
import com.zhangjihao.web.validator.EmailValidator;

/**
 * :<br>
 *
 * @author
 * @version
 * Build Time Jul 24, 2009
 */
@Controller
public class MailController extends MasterController {

 @Resource MailService mailService;
 @Resource UserService userService;

 @RequestMapping(value = "/sendEmail", method=RequestMethod.GET)
 public String sendEmail(@RequestParam(value="email",required=false) String singleEmailAddress , HttpServletRequest request){
  Email email = new Email();
  if(StringUtil.hasLength(singleEmailAddress)){
   email.setAddressee(singleEmailAddress);
  }
  request.setAttribute("email", email);
  return "system/sendMail";
 }
 
 @RequestMapping(value = "/sendEmail", method=RequestMethod.POST)
 public String send(
   @ModelAttribute Email email, //Spring MVC form
   BindingResult result,
   ModelMap model,
   HttpServletRequest request){
  try {
   new EmailValidator().validate(email, result);
   if(result.hasErrors()){
    throw new RuntimeException(" ");
   }
   if(email.getEmailGroup()!=null){
    List<User> users = userService.getUserByUserGroups(email.getEmailGroup(), "userName,email", null, null);
    StringBuffer sb = new StringBuffer(StringUtil.hasLengthBytrim(email.getAddressee()) ? (email.getAddressee().trim() + ";") : "");
    for(User user : users){
     sb.append(user.getEmail()).append(";");
    }
    email.setAddressee(sb.toString());
   }
   if(email.getAddress()==null || email.getAddress().length==0){
    request.setAttribute("message", " !");
    return "message";
   }
   
   mailService.sendMail(email); // 5 ,
   request.setAttribute("message", mailService.getMessage().append(" ").append(email.getAddress().length).append(" .").toString());
   
  } catch (Exception e) {
   request.setAttribute("message", "Has errors! The info by "+e.getMessage()+"<br/>Can log in to view more detailed information on abnormalities.");
   log.error(this.getClass().getName()+" ---------------:/n", e);
  }
  return BACK + "message";
 }
}

    一つのgetメソッド要求の接続が入ると、このコントローラは一つのhtmlページに転向します.フォームのフィールドはEmail.javaに対応しています.postメソッドが来たら、Spring MVCはフォーム中のデータをEmailオブジェクトに充填して、Mail Serviceに渡すといいです.
    最後に、最も起こりやすいエラーを述べます.
    ネット上で多くの人がJ 2 EE 5の互換性が悪いと言っています.例えば、典型的なjavamail 1.4の中で、J 2 EE 5の中でカバンとインターフェースバッグが衝突してしまいました.ユニットテストはよく次のようなエラーが発生します.
    java.lang.NoClass DefFoundError:com/sun/mail/util/BEncoderStream
    もちろん、このエラーはjavamailの実現者をプロジェクトに導入していません.(ガイドバッグがありません.)
    java.lang.NoClass DefFoundError:com/sun/mail/util/LineInputStream
    この時点ではwebコンテナさえ起動できなくなり、この2つの異常のために多くのネットユーザーがこてんこてんになっています.このようにJ 2 EE 1.4を交換すると、工事に影響を与えます.しかし、必ず概念を明らかにしてください.問題は解決しやすいです.J 2 EE 5の中でMail.jarパッケージはインターフェースだけで定義されています.実現されていません.本当にメールを送ることはできません.開発コンパイルはきっと過去に行くことができます.運行期間はサンスのJavaMail 1.4で実現してからメールを送ることができますが、なぜボスはこの二つを衝突させましたか?
    筆者の解決方法は:
    開発期間はリードしないでください.運行期間はjavamail 1.4圧縮ファイルの中のmail.jarパッケージをtomcat/libディレクトリの下に入れてください.これで完全に開発と運行ができます.ユニットテストをするなら、Java Projectを新設します.ウェブプロジェクトではなく、javamail 1.4の圧縮パッケージの中のmail.jarをプロジェクトのclassipathの下に入れることができます.