发送邮件功能包action
package action;
import java.io.File;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
/**
* 发送邮件功能类
* @author Administrator
*
*/
public class SendMail {
/**
*
* @param fjr 发件人邮箱
* @param sjr 收件人邮箱
* @param pwd 发件箱密码
* @param fwq 服务器
* @param title 主题
* @param mess 验证码信息内容
* @param file 附件
* @return 发送成功返回true
*/
public boolean send(String fjr,String sjr,String pwd,String fwq,String title,String mess,File file){
// 收件人电子邮箱
String to=sjr;
// 判断收件人的数量
Address[] address=null;
if(to.contains(";")){//如果是多个收件人
String[] str=to.split(";");
if(str.length>1){
address=new Address[str.length];
for (int i = 0; i < str.length; i++) {
try {
Address add=new InternetAddress(str[i]);
address[i]=add;
} catch (AddressException e) {
e.printStackTrace();
}
}
}
}
// 发件人电子邮箱
String from=fjr;
// 指定发送邮件的主机
String host=fwq;
// 获取系统属性
Properties properties=System.getProperties();
// 设置邮件服务器,允许授权
properties.setProperty("mail.smtp.host", host);
properties.put("mail.smtp.auth", "true");
// 获取默认session对象
Session session=Session.getDefaultInstance(properties,new Authenticator() {
public javax.mail.PasswordAuthentication getPasswordAuthentication(){
return new javax.mail.PasswordAuthentication(fjr, pwd); // 发件人邮件用户名、密码
}
});
// 创建默认的 MimeMessage 对象
MimeMessage message=new MimeMessage(session);
try {
// Set From: 头部头字段
message.setFrom(new InternetAddress(from));
// Set To: 头部头字段;判断是否为多人
if(address==null){
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
}else{
message.addRecipients(Message.RecipientType.TO, address);
}
// Set Subject: 头部头字段
message.setSubject(title);
// 判断是否有附件
if(file!=null){
// 创建消息部分
BodyPart messageBodyPart=new MimeBodyPart();
// 消息
messageBodyPart.setText(mess);
// 创建多重消息
Multipart multpart=new MimeMultipart();
// 设置文本消息部分
multpart.addBodyPart(messageBodyPart);
// 附件部分
messageBodyPart=new MimeBodyPart();
DataSource source=new FileDataSource(file.getAbsolutePath());
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(file.getName());
multpart.addBodyPart(messageBodyPart);
// 发送完整消息
message.setContent(multpart);
}else{
// 设置消息体
message.setText(mess);
}
// 发送消息
Transport.send(message);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
数据处理包userDao
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import entity.User;
import jdbc.CONN;
public class UserDao {
private Connection conn = CONN.getConn();
// 添加用户
public boolean addUser(User u) {
try {
String sql = "insert into user (Mail,password,host) values (?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, u.getMail());
ps.setString(2, u.getPwd());
ps.setString(3, u.getHost());
boolean b = ps.execute();
if (!b) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// 查询用户
public User query(String mail, String pwd) {
User u = null;
try {
String sql = "select * from user where Mail=? and password=?";
PreparedStatement ps = (PreparedStatement) conn.prepareStatement(sql);
ps.setString(1, mail);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
u = new User();
u.setMail(rs.getString("Mail"));
u.setPwd(rs.getString("password"));
u.setHost(rs.getString("host"));
}
} catch (Exception e) {
e.printStackTrace();
}
return u;
}
}
实体类user包
package entity;
public class User {
private int id;
private String mail;
private String pwd;
private String host;
public User() {
super();
}
public User(String mail, String pwd, String host) {
super();
this.mail = mail;
this.pwd = pwd;
this.host = host;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}
数据库连接包CONN
package jdbc;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
/**
* 获得数据库连接
* @author Administrator
*
*/
public class CONN {
private static String url;
private static String user;
private static String pwd;
private static Connection conn;
static{
getProp();
try {
Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection(url, user, pwd);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取配置文件
*/
public static void getProp(){
try {
InputStream is=new FileInputStream("src/config.properties");
Properties prop=new Properties();
prop.load(is);//加载配置信息
url=prop.getProperty("url");
user=prop.getProperty("user");
pwd=prop.getProperty("pwd");
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConn(){
return conn;
}
}
工具层util包
package util;
/**
* 生成6位随机数
* @author Administrator
*
*/
public class RanNum {
public int getYzm(){
return (int) (Math.random()*899999+100000);
}
public static void main(String[] args) {
System.out.println(new RanNum().getYzm());
}
}
视图层view包
定时器类
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import action.SendMail;
import entity.User;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.event.ActionEvent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
@SuppressWarnings(“serial”)
public class DTimer extends JFrame {
private JPanel contentPane;
private JSpinner spinner;//组件,微调控制器
private JLabel TimeLabel;
private String time;
/**
* Create the frame.
*/
public DTimer(String sjr,User user,String title,String text,File file) {
setTitle("\u5B9A\u65F6\u53D1\u9001");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
SpinnerDateModel model=new SpinnerDateModel();//定义显示模型
spinner = new JSpinner(model);
//编辑定义时间显示格式
JSpinner.DateEditor editor=new JSpinner.DateEditor(spinner, "yyyy-MM-dd HH:mm:ss");
spinner.setEditor(editor);//设置组件显示内容和格式
spinner.setBounds(59, 62, 137, 22);
contentPane.add(spinner);
//时间标签
TimeLabel = new JLabel("New label");
TimeLabel.setBounds(59, 119, 292, 15);
contentPane.add(TimeLabel);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
String t=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
TimeLabel.setText(t+"-->定时:"+time);
if(t.equals(time)){
boolean b=new SendMail().send(user.getMail(),sjr, user.getPwd(), user.getHost(), title, text, file);
if(b){
JOptionPane.showMessageDialog(contentPane, "发送成功");
}else{
JOptionPane.showMessageDialog(contentPane, "发送失败");
}
}
}
}, 0,1000);
//定时发送启动按钮
JButton button = new JButton("\u542F\u52A8");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Date date=(Date) spinner.getValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
time=sdf.format(date);
}
});
button.setBounds(59, 170, 93, 23);
contentPane.add(button);
}
}
登录窗体
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import action.SendMail;
import entity.User;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.event.ActionEvent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
@SuppressWarnings(“serial”)
public class DTimer extends JFrame {
private JPanel contentPane;
private JSpinner spinner;//组件,微调控制器
private JLabel TimeLabel;
private String time;
/**
* Create the frame.
*/
public DTimer(String sjr,User user,String title,String text,File file) {
setTitle("\u5B9A\u65F6\u53D1\u9001");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
SpinnerDateModel model=new SpinnerDateModel();//定义显示模型
spinner = new JSpinner(model);
//编辑定义时间显示格式
JSpinner.DateEditor editor=new JSpinner.DateEditor(spinner, "yyyy-MM-dd HH:mm:ss");
spinner.setEditor(editor);//设置组件显示内容和格式
spinner.setBounds(59, 62, 137, 22);
contentPane.add(spinner);
//时间标签
TimeLabel = new JLabel("New label");
TimeLabel.setBounds(59, 119, 292, 15);
contentPane.add(TimeLabel);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
String t=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
TimeLabel.setText(t+"-->定时:"+time);
if(t.equals(time)){
boolean b=new SendMail().send(user.getMail(),sjr, user.getPwd(), user.getHost(), title, text, file);
if(b){
JOptionPane.showMessageDialog(contentPane, "发送成功");
}else{
JOptionPane.showMessageDialog(contentPane, "发送失败");
}
}
}
}, 0,1000);
//定时发送启动按钮
JButton button = new JButton("\u542F\u52A8");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Date date=(Date) spinner.getValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
time=sdf.format(date);
}
});
button.setBounds(59, 170, 93, 23);
contentPane.add(button);
}
}
主窗体
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import action.SendMail;
import entity.User;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.JTextPane;
import javax.swing.JSpinner;
@SuppressWarnings("serial")
public class MainShow extends JFrame {
private JPanel contentPane;
private User u;
private JTextField sjrtext;
private JTextField themetext;
private JTextField fjtext;
private JFileChooser jfc;
private File file;
private JTextPane text;
private JSpinner JGtime;
private boolean isStart = false;
private JTextField JStime;
private JTextField FSmess;
private JTextField txtms;
private int temp = 0;
private boolean useHsn = false;
/**
* Create the frame.
*/
public MainShow(User user) {
// int a=JTextField.CENTER;//设置文本框居中
// setLocationRelativeTo(null);//设置窗体居中
setTitle("\u90AE\u4EF6\u53D1\u9001\u4E3B\u754C\u9762");
this.u=user;
jfc=new JFileChooser("F:\\");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 494, 394);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel label = new JLabel("\u6536\u4EF6\u4EBA\uFF1A");
label.setBounds(30, 30, 54, 15);
contentPane.add(label);
sjrtext = new JTextField();
sjrtext.setBounds(113, 27, 273, 21);
contentPane.add(sjrtext);
sjrtext.setColumns(10);
JLabel label_1 = new JLabel("\u4E3B\u9898\uFF1A");
label_1.setBounds(30, 74, 54, 15);
contentPane.add(label_1);
themetext = new JTextField();
themetext.setBounds(113, 71, 273, 21);
contentPane.add(themetext);
themetext.setColumns(10);
fjtext = new JTextField();
fjtext.setBounds(113, 112, 273, 21);
contentPane.add(fjtext);
fjtext.setColumns(10);
//添加附件按钮
JButton button = new JButton("\u6DFB\u52A0\u9644\u4EF6");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 打开文件选择对话框
int vaule=jfc.showOpenDialog(contentPane);
if(vaule==JFileChooser.APPROVE_OPTION){//如果打开文件成功值为零
// 获取选择的文件对象
file=jfc.getSelectedFile();
// 显示选择文件的文件名
fjtext.setText(file.getName());
}
}
});
button.setBounds(10, 111, 100, 23);
contentPane.add(button);
JLabel label_2 = new JLabel("\u6B63\u6587\uFF1A");
label_2.setBounds(30, 144, 54, 15);
contentPane.add(label_2);
//正文内容
text = new JTextPane();
text.setBounds(113, 143, 273, 113);
contentPane.add(text);
//发送按钮
JButton button_1 = new JButton("\u53D1\u9001");
button_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendEmail(sjrtext.getText());
}
});
button_1.setBounds(10, 306, 93, 23);
contentPane.add(button_1);
//定时发送
JButton button_2 = new JButton("\u5B9A\u65F6\u53D1\u9001");
button_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new DTimer(sjrtext.getText(),user,themetext.getText(),text.getText(),file).setVisible(true);
}
});
button_2.setBounds(113, 306, 93, 23);
contentPane.add(button_2);
//发送时间间隔
JGtime = new JSpinner();
JGtime.setValue(1000);//设置时间间隔默认初始值为1秒
JGtime.setBounds(402, 307, 66, 22);
contentPane.add(JGtime);
//呼死你发送
JButton start = new JButton("\u547C\u6B7B\u4F60\u53D1\u9001");
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!isStart){
start.setText("停止");
isStart=true;
useHsn=true;//使用呼死你发送开关
}else{
start.setText("启动呼死你");
isStart=false;
useHsn=false;//默认没有使用呼死你发送
}
int i=(int) JGtime.getValue();
start(i);
}
});
start.setBounds(225, 306, 93, 23);
contentPane.add(start);
//时间计时器
JStime = new JTextField();
JStime.setBounds(61, 275, 100, 21);
contentPane.add(JStime);
JStime.setColumns(10);
//发送邮件的反馈信息
FSmess = new JTextField();
FSmess.setBounds(195, 275, 209, 21);
contentPane.add(FSmess);
FSmess.setColumns(10);
//间隔(ms):
txtms = new JTextField();
txtms.setText(" \u95F4\u9694(ms):");
txtms.setBounds(321, 307, 71, 21);
contentPane.add(txtms);
txtms.setColumns(10);
}
/**
* 发送邮件的方法
* @param to
*/
public void sendEmail(String to){
String title=themetext.getText();
String textzw=text.getText();
boolean b=new SendMail().send(u.getMail(), to, u.getPwd(), u.getHost(), title, textzw, file);
if(b){
if(!useHsn)//如果没有使用呼死你发送邮件,提示“发送成功”,否则不提示
JOptionPane.showMessageDialog(contentPane, "发送成功");
}else{
JOptionPane.showMessageDialog(contentPane, "发送失败");
}
}
/**
* 启动呼死你的方法
*/
public void start(int i){
/**
* 计数器线程
*/
new Timer().schedule(new TimerTask() {
int h = 0;
int m = 0;
int s = 0;
@Override
public void run() {
if(isStart){
s++;
if(s==60){
m++;
s=0;
}
if(m==60){
h++;
m=0;
}
JStime.setText(h+":"+m+":"+s);
JStime.setLayout(new FlowLayout(FlowLayout.CENTER));//设置文本框居中
}else{//取消当前计数任务
cancel();
}
}
}, 0, 1000);
/**
* 邮件发送反馈结果线程
*/
new Timer().schedule(new TimerTask() {
@Override
public void run() {
if(isStart){
MainShow.this.sendEmail(sjrtext.getText());
temp++;
FSmess.setText("正在发送第"+temp+"封邮件");
}else{
temp=0;
cancel();//取消当前计数线程
}
}
}, 0, i);
}
}
注册窗体
package view;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import action.SendMail;
import entity.User;
import util.RanNum;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import java.awt.Font;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;
/**
* 用户注册界面
* @author Administrator
*
*/
@SuppressWarnings("serial")
public class RegShow extends JFrame {
private JPanel contentPane;
private JTextField mailtext;
private JPasswordField pwdtext;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
RegShow frame = new RegShow();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public RegShow() {
setTitle("\u7528\u6237\u6CE8\u518C");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblMail = new JLabel("Mail:");
lblMail.setFont(new Font("楷体", Font.ITALIC, 15));
lblMail.setBounds(40, 38, 58, 25);
contentPane.add(lblMail);
JLabel lblPassword = new JLabel("password:");
lblPassword.setFont(new Font("楷体", Font.ITALIC, 15));
lblPassword.setBounds(40, 87, 84, 25);
contentPane.add(lblPassword);
JLabel lblHost = new JLabel("host:");
lblHost.setFont(new Font("楷体", Font.ITALIC, 15));
lblHost.setBounds(40, 140, 84, 25);
contentPane.add(lblHost);
mailtext = new JTextField();
mailtext.setBounds(135, 40, 247, 21);
contentPane.add(mailtext);
mailtext.setColumns(10);
pwdtext = new JPasswordField();
pwdtext.setBounds(134, 89, 248, 21);
contentPane.add(pwdtext);
JComboBox hosttext = new JComboBox();
hosttext.setModel(new DefaultComboBoxModel(new String[] {"smtp.exmail.qq.com", "smtp.126.com", "smtp.163.com"}));
hosttext.setBounds(135, 144, 174, 21);
contentPane.add(hosttext);
JButton btnNewButton = new JButton("\u63D0\u4EA4");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
int Yzm=new RanNum().getYzm();//生成验证码
String password=String.valueOf(pwdtext.getPassword());//提取密码
String sjrmail=mailtext.getText();//收件人邮箱地址
String host=(String) hosttext.getSelectedItem();//提取主机类型
String fjrmail=sjrmail;//发件人等于收件人,自己给自己发邮件
String fwq=host;//选择服务器类型邮箱
String title="呼死你邮件系统用户注册验证邮件";
String message= "您的验证码为:"+Yzm;
User u=new User(sjrmail,password,fwq);//实例化user并初始化
boolean b=new SendMail().send(fjrmail, sjrmail, password, fwq, title, message, null);
if(b){
new YanZheng(Yzm,u).setVisible(true);
}else{
JOptionPane.showMessageDialog(contentPane, "邮箱验证失败!");
}
}
});
btnNewButton.setBounds(181, 196, 93, 23);
contentPane.add(btnNewButton);
}
}
验证窗体
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import dao.UserDao;
import entity.User;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.util.TimerTask;
import java.awt.event.ActionEvent;
@SuppressWarnings("serial")
public class YanZheng extends JFrame {
private JPanel contentPane;
private JTextField yzm;
private int second = 30;
private int yznum;
/**
* Create the frame.
*/
public YanZheng(int i,User u) {
this.yznum=i;
String s=String.valueOf(yznum);
setTitle("\u90AE\u7BB1\u9A8C\u8BC1");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 371, 232);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblNewLabel = new JLabel("\u8BF7\u8F93\u5165\u90AE\u7BB1\u9A8C\u8BC1\u7801\uFF1A");
lblNewLabel.setBounds(42, 43, 108, 15);
contentPane.add(lblNewLabel);
yzm = new JTextField();
yzm.setBounds(175, 40, 157, 21);
contentPane.add(yzm);
yzm.setColumns(10);
JLabel djs = new JLabel("");
djs.setBounds(42, 85, 208, 15);
contentPane.add(djs);
//倒计时
new java.util.Timer().schedule(new TimerTask() {
@Override
public void run() {
if(second>0){
djs.setText("验证码在"+(--second)+"内有效");
}
}
}, 0,1000);
JButton yzButton = new JButton("\u9A8C\u8BC1");
yzButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String yz=yzm.getText();
if(second>0){
if(yz.equals(s)){//验证成功
JOptionPane.showMessageDialog(contentPane, "验证成功!");
boolean b=new UserDao().addUser(u);
if(b){
JOptionPane.showMessageDialog(contentPane, "注册成功!");
YanZheng.this.dispose();
}else{
JOptionPane.showMessageDialog(contentPane, "注册失败!");
}
}else{
JOptionPane.showMessageDialog(contentPane, "验证码输入错误!");
}
}else{
JOptionPane.showConfirmDialog(contentPane, "验证码已过期,请重新发送!");
}
}
});
yzButton.setBounds(137, 126, 93, 23);
contentPane.add(yzButton);
}
}
配置信息config.properties
url=jdbc:mysql://localhost:3306/mail
user=root
pwd=root