12月10日——培训第18天

今天是张老师讲javaMail……

-----------------------------
问题:邮件发送应用场所?我们是不是要写一个邮件服务器,就像类似于
outlook那样的东东?或是新浪的邮件服务器?

并不完全是这样,事实是,网站上的会员得到管理员的消息就是属于邮件服务的
一种应用,会员之间的消息传送也是基于这种机制。还有,登陆密码通过邮件发送
到用户的邮箱也是。

邮件还可以图文并茂,或是带附件之类,也就是格式较为复杂的邮件

我登陆新浪的邮箱给搜狐发邮件,先要连接新浪服务器,然后新郎服务器再去连接
搜狐,新浪将邮件投递给搜狐的smtp的服务器,搜狐这边是smtp服务器从新浪那边
把邮件收过来然后保存到pop3服务器中。也就是新浪把邮件给搜狐用的也是smtp,客户
投递邮件给服务器也是smtp,客户从pop3服务器中拿邮件。

在outlookExpress中可以设置smtp和pop3邮件服务器。

标准协议中,发送邮件是不需要检查用户身份的,也就是不需要登陆,但是现在都需要
登陆确认是否自己的用户,是自己的用户才能够使用自己的邮件服务器,所以安全密码是
绝对不可以为空的。

用户固然要把新浪的smtp服务器地址交给outlook,
但是新浪要把邮件发送到搜狐的话,也需要想办法得到搜狐的smtp服务器的地址,但是
新浪smtp服务器发送邮件的话是要发往世界各地成千上万的邮件服务器的,不可能每一次
发送都去主动的找对方的smtp服务器,毕竟smtp服务器太多了,每天都有新的smtp服务器诞生。

所以使用一种方式能够自动得到对方的smtp服务器的地址。

安装邮件服务器软件,这样首先可以发送邮件了,但是如何能让别人找到我呢?
DNS域名服务器:将网址翻译成为ip地址。
发送邮件给对方时,首先要得到对方域里面的邮件服务器才可以。DNS服务器中记录了很多信息,
比如主机名对应的ip,以及这个域的邮件服务器是什么

每一个域都有一个DNS服务器,里面要记载这个域下面的子域信息,
比如training.itcast.cn
itcast.cn域下包含有一个training子域。

cn记录自己以下的包括itcast等地的域,itcast.cn记录它以下的包括mai和training的域

其实就是通过DNS来定位的。
------------------------------------------

IMAP是对pop3协议的扩展
如果ip地址可以访问,但是域名不能访问的话,那就是DNS服务器的问题了

比如要访问下面的主机:
www.wh.hb.cn

首先客户机要去查询DNS查询服务器

DNS查询服务器作如下工作:
首先找管理根域的DNS服务器返回cn域的DNS,
然后查找管理“cn”域的DNS服务器返回“hb.cn”的DNS,
再去找管理“hb.cn”的DNS返回“wh.hb.cn”的DNS,
最好找管理“wh.hb.cn”的DNS最终返回www.wh.hb.cn主机名的ip地址

这样就找到了这个ip,就可以访问了。

动态DNS:每一次上网的时候都有个不同的ip,每次上网都动态的将这个ip绑定自己申请的域名
这样每次别人访问的时候就不是读缓存,而是直接去找这个动态DNS去解析ip。
--------------------------------------

新浪是如何得到itcast或是搜狐的邮件服务器的?
命令行中输入:
nslookup
即可得到当前的域名服务器和本机ip

然后输入set type=mx回车后输入itcast.cn
再次回车后可以得到
mail.itcast.cn的ip

输入sina.com可以得到新浪的三个邮件服务器。
注意这三个邮件服务器是轮循的

邮件服务器对于其他邮件服务器的请求是不加以验证的,但是对客户的请求是加以验证的。
也就是对待其他邮件服务器用一个smtp服务器,对待一般用户的话用另一个smtp服务器

nslookup->
set type=mx->

sina.com->

telnet smtp.sina.com.cn 25->

我们只需要发送的协议smtp就可以了,pop3在我们的网站应用中是不必要的。

发送附件的邮件:
使用multipart
MIME邮件三种组合方式
multipart/mixed  混合 (内部有好几部分组成,有分隔符,例如:------=_NextPart_000_0000_“随机码”)
multipart/related 相关联的
multipart/alternative 选择

用java程序向外发送邮件的话,如果不用第三方api的话,首先要连接对方的stmp服务器,然后ehlo,mailfrom,
rcpt,data等等命令,发送多封邮件给一个人,发送一封邮件给n个人,

邮件编码的两种格式:quoted-printable和Base64
Content-Transfer-Encoding: quoted-printable

生成这些复杂邮件需要调用别人提供的复杂函数api,第二是发送邮件,也有api和函数等;
Sun公司已经提供了邮件包:

mail.jar:用于简单邮件
activation.jar:是java激活框架,用于复杂邮件,每一个操作都是一个java类。

去java.sun.com里面去下载javaMail的最新文档即可。解压后的lib里面是jar包
-----------------------------------------------

下午课程开始:
javaMail解压后的目录不要放在中文或是有空格的文件夹下!!

先看readme,
需要JavaMail和service provider(就是jar包)

JDBC程序中,如果没有数据库驱动jar包的话,编译是可以通过的,但是运行是过不了的。

java -Djdbc.drivers=com.oracle.Driver:com.mysql.jdbc.Driver
这样的话就不必在程序中写Class.forName("com.oracle.Driver");
程序运行时会先尝试com.oracle.Driver,不行的话尝试com.mysql.jdbc.Driver

写javaMail的时候也是一样,只需要javaMail中的api,也就是
lib/mailapi.jar:包含了所有javaMail的api
lib/smtp.jar:包含了smtp服务的支持
上述两个jar包缺一不可

当然还有lib/imap.jar和lib/pop3.jar

文档的docs中的javadocs里面有详细的接口,就像jdk中的格式一样。

javax.mail:它是抽象类。

addFrom(new InternetAddress("")); 添加寄信人地址

class Outer
{
 class Inner
 {
 };
};

public class Test
{
 pubilc static void main(String[] args)
 {
  Outer out = new Outer();
  Outer.Inner in = out.new Inner();
  //Outer.Inner in = new Outer.Inner(); 当Inner是静态类的时候才能这么作!
  System.out.println(in);
  System.out.println(Integer.toHexString(in.hashCode()));
  //这里有一点需要注意,如果打印某对象的话,那么打印出的字符串末尾的一串奇怪的字符串其实就是
  //该对象的哈希码的值转换为16进制后的产物……可以对比上面两句话的结果。
 }
};
-------------------
实体类toString的重写:
pubilc class Person
{
 private String name ;
 private int age ;

 public String toString()
 {
  return name + ":" + age ;
  }
}

System.out.println(new Person());
注意:我们打印一个对象,其实打印的是这个对象的toString方法的返回值!!!

Interface Test
{
 void func();
 
}

class Outer
{
 public int age;
 class Inner implements Test
 {
  void func()
  {
   System.out.println(age);
  }
 };

 Test getTestInstance()
 {
  return new Inner();  //因为Inner类继承了Test接口,所以方法的返回值可以是Test类型引用
 }
};

注意上面的结构是很常用的:通过让内部类实现一个接口,然后在外部类中定义返回该内部类对象的
针对于接口引用的方法,以后实现内部类的对象就可以用下面的方法来实现了:
这样就可以调用如下语句了:
Test in = new Outer().getTestInstance();
in.func();

上面的应用也是典型的那种“没有变量指向外部类,但是有变量指向内部类”的情况,应该能够自己
独立的把这道题目背着写出来。能返回内部类给别人用,但是别人又不用知道我的内部类的名字……

这也就是面向接口编程,而不是面向类编程了。

假如一个外部类中的内部类是静态的,这个内部类也就是可以当作外部类来使用了,只不过这个内部类
引用时叫做“外部类名.内部类名”
-----------------------------
Message.RecipientType也是属于这种外部类中的内部静态类!

Color
{
 private int red ;
 private int green ;
 private int blue ;
 public Color(int red, int green , int blue)
 {
 
 }
 
 public static Color RED = new Color(255,0,0);

 
}

上面的Color.RED的调用方式就类似于Message.RecipientType.TO

Message:创建邮件的javaMail 的API
Transport是发送邮件的JavaMail的API
接收的是Store

printWriter作用:将9加上一个字符'0'(asc码是57),然后输出字符'9'
    也就是把数字转换成对应的数字字符

printWriter接收一个字符串,再将其转变为字节数组,然后弄到IO流中去。

-----------------------------------

printWriter工作原理(在Servlet中很有用的!)

根据session的信息构建出MimeMessage(抽象类Message的子类实现)

关于setRecipients方法:主要是里面的第二个参数有两种情况,第一种是接收一个地址类的数组,
第二种情况是要接收一个地址字符串,都是指明收件人的地址;第一个参数是指明是抄送还是暗送还是一般的送。

msg.setRecipients(Message.RecipienType.TO,InternetAddress.parse("it315@sohu.com"))

注意上面的第二个参数是个address数组,我们也可以这么改写上面的第二个参数:
  new InternetAddress[]{new InternetAddress("it315@sohu.com")}
这也是个地址类的数组

1 setRecipients(Message.RecipientType type InternetAddress[] addresses)
{}

2 setRecipients(Message.RecipientType type java.lang.String addresses)
{
 setRecipients(type,InternetAddress.parse(addresses)); //这是借助了已经实现了的第一个类
 //注意这里的InternetAddress.parse(addresses)方法是把字符串address转换成了address数组!也就是把实现第二个类的
 //工作交给了第一个类去做!
}

-------------------------------------
javaMail中的Session是个final型的抽象类,里面有一个getDefaultInstance方法,
可以返回Session的对象,但是问题是……底层是如何实现的呢?final说明绝对不可以继承,
既然不能继承的话,这个抽象类是如何被实例化出来的呢???

汗……张老师也不知道……

复杂邮件是要有MimeMultipart,其中要有bodyMultipart
-------------------------------------
今天讲得有些零散了,还需要30分钟得时间才能把今天的任务完成

MimeBodyPart中的setHeader方法和set
假如对像是gifBodyPart
gifBodyPart.setContentID("image");
gifBodyPart.setHeader("Content-Id","image");                

上面两种方法是等价的。

张老师的两段邮件源代码发了下来……

以前一班讲javaMail没有像我们这么详细,他们弯路走的比较多,请了很多外面公司里面的人来讲,
但是呢,他们有时候讲着讲着自己都没有讲清楚,学员也迷糊……也许我们的情况稍微好点把……
-----------------------------------------------

张老师书稿上的两段源代码:

1 简单邮件:

import java.util.Date;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class TextMessage
{
 public static void main(String[] args) throws Exception
 {
  String from = "it315_test@sina.com";
  String to = "it315_test@sohu.com";
  String subject = "test";
  String body = "test!!!";
  
  Session session = Session.getDefaultInstance(new Properties());
  MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(from));//设置发件人
        msg.setRecipients(Message.RecipientType.TO,
       InternetAddress.parse(to));//设置收件人
        msg.setSentDate(new Date());//设置发送日期
        msg.setSubject(subject);//设置邮件主题
        msg.setText(body); //设置邮件内容
        msg.saveChanges();//存储邮件信息
       
  //把MimeMessage对象中的内容写入到文件中
        msg.writeTo(new FileOutputStream("c://test.eml"));  
 }

 

2 复杂邮件:

import java.io.FileOutputStream;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

public class HtmlMessage
{
 public static void main(String[] args) throws Exception
 {
        String from = "it315_test@sohu.com ";
        String to = "it315_test@sohu.com "; 
        String subject = "HTML邮件";
        String body = "<a href=http://www.it315.org>" _fcksavedurl="http://www.it315.org>"" +
            "欢迎大家访问我们的网站</a></br>" +
      "</br><img src=/"cid:image/">";
  
        Session session = Session.getDefaultInstance(new Properties());
        //创建MimeMessage对象,并设置它的消息头
  MimeMessage message = new MimeMessage(session);  //session改为null,如何?
        message.setFrom(new InternetAddress(from));
        message.setRecipients(Message.RecipientType.TO,
          InternetAddress.parse(to));
        message.setSubject(subject);
       
        //设置一个保存邮件体的容器对象MimeMultipart,内容类型为"related"
        MimeMultipart multipart = new MimeMultipart("related");
      
  //设置保存HTML消息的MimeBodyPart对象,并把它保存到容器对象中
        MimeBodyPart htmlBodyPart = new MimeBodyPart();         
        htmlBodyPart.setContent(body,"text/html;charset=gb2312");
        multipart.addBodyPart(htmlBodyPart);
 
  //设置保存图片文件的MimeBodyPart对象,并把它保存到容器对象中
        MimeBodyPart gifBodyPart = new MimeBodyPart();
        FileDataSource fds = new FileDataSource("d://image//slogo.gif");
        gifBodyPart.setDataHandler(new DataHandler(fds));
        gifBodyPart.setContentID("image1");
  gifBodyPart.setHeader("Content-Id","image1");
        multipart.addBodyPart(gifBodyPart);
  
  //设置容器对象到MimeMessage对象中,并调用saveChanges方法更新
        message.setContent(multipart);
        message.saveChanges();
       
  //把MimeMessage对象中的内容写入到文件中
        message.writeTo(new FileOutputStream("c://htmlMessage.eml"));
 }
}

 

-----------------------------------------
张老师自己的总结:


1.由Message.RecipientType引出内部类知识的讲解:
想想"没有变量引用外部类,但有变量引用其中的内部类"的效果是通过怎样的代码产生的?
Interface Xxx
{
 void func();
}

class Outer
{
 int age;
 //内部类通常应实现一个接口,这样外部的引用变量的类型就可以定义成那个接口
 public class Inner implements Xxx
 {
  void func()
  {
   System.out.println(age);
  }
 }
 
 // 只需将返回的类型定义接口类型,从而可以隐藏具体的实现类名,增强可扩展性 
 //面向接口编程,不要面向类编程
 Xxx getXxxInstance()
 {
  return new Inner();
 }
}

Xxx in = new Outer().getXxxIntance();
...
in.func
//内部类能够访问外部类的成员变量,那一定有个外部类对象存储!

public class Test
{
 public static void main(String [] args)
 {
  /*Outer.Inner in = new Outer.Inner();
  System.out.println(in);*/
  Outer out = new Outer();
  Outer.Inner in = out.new Inner();
  System.out.println(in);
  System.out.println(Integer.toHexString(in.hashCode()));
 }
}

讲解上面代码时引出了打印一个对象的原理,实现toString方法的理由。
还引出了静态内部类,静态内部类不能访问外部类的非静态成员变量的理由。

2.想想JDK中的java.awt.Color类中定义的常量的代码是怎样的?
Color
{
 public Color(int red,int green,int blue)
 {
 }
 
 public static Color RED = new Color(255,0,0);
}
由此类推,不难明白Message.RecipientType中定义的TO、CC、BCC常量是怎么回事了?


3.由new MIMEMessage()必须接受一个Session对象作为参数,觉得不太合理,而引出PrintWriter的原理与其在
JDK 1.4版本时的设计缺陷

4.由下面的代码引出一种替代方式,关键是要自己能够写出这种替代方式的代码!
    msg.setRecipients(Message.RecipientType.TO,
    InternetAddress.parse("it315@sohu.com"));
    
  msg.setRecipients(Message.RecipientType.TO,
    new InternetAddress[]{new InternetAddress("it315@sohu.com")});
 由看setRecipients方法的帮助文档,
    setRecipients(Message.RecipientType type, InternetAddress [] addresses)
   {
   }
 同时看到一种重载形式:  
    setRecipients(Message.RecipientType type, java.lang.String addresses)
 讲解了这个重载方法内部的实现细节,让大家知道一种思想(有一个方法做具体细节,其他重载方法
  对参数进行一些方便转换后,再调用那个方法)  
    setRecipients(Message.RecipientType type, java.lang.String addresses)
    {
      //内部还是调用上面的那个方法,只是把参数转了一下,以便为外面提供更多的操作手段!
      setsetRecipients(type,InternetAddress.parse(addresses));
    }
  
5.MimeBodyPart类的setContentId实际上内部也是调用SetHeader方法

作业1:生成一个带图片的邮件,分别用Content-Location头和Content-Id来实现。
作业2:想想"没有变量引用外部类,但有变量引用其中的内部类"的效果是通过怎样的代码产生的?

 

 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值