锘??xml version="1.0" encoding="utf-8" standalone="yes"?>
銆銆鍦╫rg.springframework.sample.jpetstore鍖呬腑錛?BR>銆銆Account綾伙細姣旇緝綆鍗曪紝鏅氱殑JavaBean銆?BR>銆銆Cart綾誨拰CarItem綾伙細浠庡叧緋繪ā鍨嬩腑錛屽彲浠ョ湅鍒拌繖鏄竴涓富浠庡叧緋葷殑瀵硅薄銆傚叾涓瑿art錛堣喘鐗╄濺錛変腑鏈変笉灝戣涓恒備緥濡傦細鏂板銆佸垹闄よ喘鐗╄濺欏圭洰錛涜綆楁婚噾棰濄備竴鑸潵璇達紝鎴戜滑欏圭洰涓竴鑸繖鏍風殑閫昏緫鏄啓鍦∕anager綾諱腑鐨勩?BR>銆銆鍦∣rder綾諱腑錛屼篃閲囩敤涓浜涗笟鍔¤涓猴紝渚嬪鍒濆鍖栬鍗曠瓑絳夈?BR>銆銆
Spring 鏀寔涓涓洿楂樺眰鐨勬娊璞$敤鏉ュ彂閫佺數瀛愰偖浠訛紝瀹冮殣钘忓簳灞傞偖浠剁郴緇熺殑緇嗚妭騫朵笖浠h〃瀹㈡埛绔浣庣駭鍒殑鎺у埗 銆?/P>
18.2. Spring mail abstraction structure
Spring閭歡鎶借薄緇撴瀯
The main package of Spring mail abstraction layer is org.springframework.mail package. It contains central interface for sending emails called MailSender and the value object which encapsulates properties of a simple mail such as from, to, cc, subject, text called SimpleMailMessage. This package also contains a hierarchy of checked exceptions which provide a higher level of abstraction over the lower level mail system exceptions with the root exception being MailException.Please refer to JavaDocs for more information on mail exception hierarchy.
Sring閭歡鎶借薄灞傜殑涓昏鍖呮槸錛歰rg.springframework.mail 鍖呫傚畠鍖呭惈鍙玀ailSender涓哄彂閫侀偖浠剁殑鏍稿績鎺ュ彛鍜屽寘鍚畝鍗曢偖浠跺睘鎬т緥濡俧rom,to,cc,subject,text鍙玈impleMailMessage鐨勫煎璞? 榪欎釜鍖呬篃鍖呭惈涓涓鏌ュ紓甯哥殑灞傛錛屽畠鏀寔涓涓洿楂樼駭鍒殑鎶借薄瓚呰繃浣庣駭鍒殑閭歡緋葷粺寮傚父浼撮殢鏍瑰紓甯稿瓨鍦∕ailException. 璇峰弬鑰僇avaDocs涓烘洿澶氱殑淇℃伅鏉傞偖浠跺紓甯稿眰嬈°?/P>
Spring also provides a sub-interface of MailSender for specialized JavaMail features such as MIME messages, namely org.springframework.mail.javamail.JavaMailSender It also provides a callback interface for preparation of JavaMail MIME messages, namely org.springframework.mail.javamail.MimeMessagePreparator
Spring涔熸敮鎸佷竴涓狹ailSender鐨勪笓鐢ㄤ簬JavaMail鐗瑰緛渚嬪MIME娑堟伅瀛愭帴鍙o紝鍛藉悕涓簅rg.springframework.javamail.JavaMailerSener銆傚畠涔熸敮鎸佷竴涓負JavaMail MIME淇℃伅鐨勫噯澶囧洖璋冩帴鍙o紝鍛藉悕涓簅rg.springframework.mail.JavaMail.MimeMessagePreparator.
MailSender:
public interface MailSender {
/**
* Send the given simple mail message.
* @param simpleMessage message to send
* @throws MailException in case of message, authentication, or send errors
* 鍙戦佺粰瀹氱殑綆鍗曢偖浠朵俊鎭?BR> * @鍙傛暟 simpleMessage 鍙戦佺殑淇℃伅
* @throws MailException 鍋囪淇℃伅錛岃瘉鏄庢垨鍙戦侀敊璇?BR> */
public void send(SimpleMailMessage simpleMessage) throws MailException;
/**
* Send the given array of simple mail messages in batch.
* @param simpleMessages messages to send
* @throws MailException in case of message, authentication, or send errors
*/
public void send(SimpleMailMessage[] simpleMessages) throws MailException;
}
JavaMailSender:
public interface JavaMailSender extends MailSender {
/**
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
* of this sender. Needs to be called to create MimeMessage instances
* that can be prepared by the client and passed to send(MimeMessage).
* @return the new MimeMessage instance
* @see #send(MimeMessage)
* @see #send(MimeMessage[])
* 鍒涘緩涓涓柊鐨凧avaMail MimeMessage 涓烘綔鍦ㄧ殑JavaMail鐨勫彂閫佽呯殑浼氳瘽.
* 闇瑕佽璋冪敤鏉ュ垱寤篗imeMessage瀹炰緥錛屽畠鍙互琚鎴峰噯澶囧茍涓旇浼犻掑彂閫?MimeMessage).
* @return 榪欎釜鏂扮殑MimeMessage 瀹炰緥
* @see #send(Message)
* @sess #send(MimeMessage[])
*/
public MimeMessage createMimeMessage();
/**
* Send the given JavaMail MIME message.
* The message needs to have been created with createMimeMessage.
* @param mimeMessage message to send
* @throws MailException in case of message, authentication, or send errors
* @see #createMimeMessage
*/
public void send(MimeMessage mimeMessage) throws MailException;
/**
* Send the given array of JavaMail MIME messages in batch.
* The messages need to have been created with createMimeMessage.
* @param mimeMessages messages to send
* @throws MailException in case of message, authentication, or send errors
* @see #createMimeMessage
*/
public void send(MimeMessage[] mimeMessages) throws MailException;
/**
* Send the JavaMail MIME message prepared by the given MimeMessagePreparator.
* Alternative way to prepare MimeMessage instances, instead of createMimeMessage
* and send(MimeMessage) calls. Takes care of proper exception conversion.
* @param mimeMessagePreparator the preparator to use
* @throws MailException in case of message, authentication, or send errors
*/
public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException;
/**
* Send the JavaMail MIME messages prepared by the given MimeMessagePreparators.
* Alternative way to prepare MimeMessage instances, instead of createMimeMessage
* and send(MimeMessage[]) calls. Takes care of proper exception conversion.
* @param mimeMessagePreparators the preparator to use
* @throws MailException in case of message, authentication, or send errors
*/
public void send(MimeMessagePreparator[] mimeMessagePreparators) throws MailException;
}
MimeMessagePreparator:
public interface MimeMessagePreparator {
/**
* Prepare the given new MimeMessage instance.
* @param mimeMessage the message to prepare
* @throws MessagingException passing any exceptions thrown by MimeMessage
* methods through for automatic conversion to the MailException hierarchy
*/
void prepare(MimeMessage mimeMessage) throws MessagingException;
}
18.3. Using Spring mail abstraction
浣跨敤Spring閭歡鎶借薄
Let's assume there is a business interface called OrderManager
璁╂垜浠亣瀹氳繖閲屾湁涓涓晢涓氭帴鍙e彨OrderManager
public interface OrderManager {
void placeOrder(Order order);
}
and there is a use case that says that an email message with order number would need to be generated and sent to a customer placing that order. So for this purpose we want to use MailSender and SimpleMailMessage
騫朵笖榪欓噷鏈変竴涓湁鐢ㄦ渚嬶紝鍙互璇翠竴涓即闅忚鍗曠紪鍙風殑閭歡淇℃伅灝嗛渶瑕佽浜х敓騫朵笖鍙戦佺粰涓涓鎴峰鐞嗚繖涓鍗曘傛墍浠ヤ負榪欎釜鐩殑鎴戜滑鎯寵浣跨敤MailSender鍜孲impleMailSender.
Please note that as usual, we work with interfaces in the business code and let Spring IoC container take care of wiring of all the collaborators for us.
璇鋒敞鎰忕収甯革紝鎴戜滑宸ヤ綔浣跨敤鍦ㄥ晢涓氫唬鐮佷腑鐨勬帴鍙e茍涓旇Spring Ioc 瀹瑰櫒鍏沖績涓烘垜浠殑鎵鏈夊悎浣滆呫?/P>
Here is the implementation of OrderManager
榪欓噷鏄疧rderManager鐨勫疄鐜?
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
public class OrderManagerImpl implements OrderManager {
private MailSender mailSender;
private SimpleMailMessage message;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setMessage(SimpleMailMessage message) {
this.message = message;
}
public void placeOrder(Order order) {
//... * Do the business calculations....
//... * Call the collaborators to persist the order
//Create a thread safe "sandbox" of the message
SimpleMailMessage msg = new SimpleMailMessage(this.message);
msg.setTo(order.getCustomer().getEmailAddress());
msg.setText(
"Dear "
+ order.getCustomer().getFirstName()
+ order.getCustomer().getLastName()
+ ", thank you for placing order. Your order number is "
+ order.getOrderNumber());
try{
mailSender.send(msg);
}
catch(MailException ex) {
//log it and go on
System.err.println(ex.getMessage());
}
}
}
Here is what the bean definitions for the code above would look like:
榪欓噷鏄繖涓負榪欎釜浠ヤ笂浠g爜bean瀹氫箟綾諱技錛?/P>
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host"><value>mail.mycompany.com</value></property>
</bean>
<bean id="mailMessage"
class="org.springframework.mail.SimpleMailMessage">
<property name="from"><value>customerservice@mycompany.com</value></property>
<property name="subject"><value>Your order</value></property>
</bean>
<bean id="orderManager"
class="com.mycompany.businessapp.support.OrderManagerImpl">
<property name="mailSender"><ref bean="mailSender"/></property>
<property name="message"><ref bean="mailMessage"/></property>
</bean>
Here is the implementation of OrderManager using MimeMessagePreparator callback interface. Please note that the mailSender property is of type JavaMailSender in this case in order to be able to use JavaMail MimeMessage:
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;
public class OrderManagerImpl implements OrderManager {
private JavaMailSender mailSender;
public void setMailSender(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void placeOrder(final Order order) {
//... * Do the business calculations....
//... * Call the collaborators to persist the order
MimeMessagePreparator preparator = new MimeMessagePreparator() {
public void prepare(MimeMessage mimeMessage) throws MessagingException {
mimeMessage.setRecipient(Message.RecipientType.TO,
new InternetAddress(order.getCustomer().getEmailAddress()));
mimeMessage.setFrom(new InternetAddress("mail@mycompany.com"));
mimeMessage.setText(
"Dear "
+ order.getCustomer().getFirstName()
+ order.getCustomer().getLastName()
+ ", thank you for placing order. Your order number is "
+ order.getOrderNumber());
}
};
try{
mailSender.send(preparator);
}
catch(MailException ex) {
//log it and go on
System.err.println(ex.getMessage());
}
}
}
If you want to use JavaMail MimeMessage to the full power, the MimeMessagePreparator is available at your fingertips.
濡傛灉浣犳兂浣跨敤JavaMail MimeMessage鏉ヤ嬌寰楄凍澶熷己澶э紝MimeMessagePreparator 鏄彲浠ュ埄鐢ㄧ殑銆?/P>
Please note that the mail code is a crosscutting錛堟í鍒囩殑錛?concern錛堝叧娉級 and is a perfect candidate錛堝欓夛級 for refactoring into a custom Spring AOP advice, which then could easily be applied to OrderManager target. Please see the AOP chapter.
18.3.1. Pluggable MailSender implementations
Spring comes with two MailSender implementations out of the box - the JavaMail implementation and the implementation on top of Jason Hunter's MailMessage class that's included in http://servlets.com/cos (com.oreilly.servlet). Please refer to JavaDocs for more information.
18.4. Using the JavaMail MimeMessageHelper
One of the components that comes in pretty handy when dealing with JavaMail messages is the org.springframework.mail.javamail.MimeMessageHelper. It prevents you from having to use the nasty APIs the the javax.mail.internet classes. A couple of possible scenarios:
18.4.1. Creating a simple MimeMessage and sending it
Using the MimeMessageHelper it's pretty easy to setup and send a MimeMessage:
// of course you would setup the mail sender using
// DI in any real-world cases
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMesage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setTo("test@host.com");
helper.setText("Thank you for ordering!");
sender.send(message);
18.4.2. Sending attachments and inline resources
Email allow for attachments, but also for inline resources in multipart messages. Inline resources could for example be images or stylesheet you want to use in your message, but don't want displayed as attachment. The following shows you how to use the MimeMessageHelper to send an email along with an inline image.
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMesage();
// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");
// use the true flag to indicate the text included is HTML
helper.setText(
"<html><body><img src='cid:identifier1234'></body></html>"
true);
// let's include the infamous windows Sample file (this time copied to c:/)
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);
// if you would need to include the file as an attachment, use
// addAttachment() methods on the MimeMessageHelper
sender.send(message);
Inline resources are added to the mime message using the Content-ID specified as you've seen just now (identifier1234 in this case). The order in which you're adding the text and the resource are VERY important. First add the text and after that the resources. If you're doing it the other way around, it won't work!
<bean id="methodInvokingJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"><ref bean="exampleBusinessObject"/></property>
<property name="targetMethod"><value>doIt</value></property>
</bean>
The above example will result in the doIt being called on the exampleBusinessObject (see below):
public class BusinessObject {
// properties and collaborators
public void doIt() {
// do the actual work
}
}
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
Using the MethodInvokingJobDetailFactoryBean you don't need to create one-line jobs that just invoke a method, and you only need to create the actual business object and wire up the detail object.
浣跨敤MethodInvokingJobDetailFactoryBean 浣犱笉闇瑕佸垱寤轟竴涓湪綰跨殑jobs錛屼粎浠呰皟鐢ㄥ畠鐨勬柟娉曪紝浣犲彲浠ヤ粎浠呭彧闇瑕佸垱寤轟竴涓疄闄呯殑閫昏緫瀵硅薄騫朵笖鎶婂畠緇戝畾鍒扮粏鑺傚璞°?/P>
By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering with each other. If you specify two triggers for the same JobDetail, it might be possible that before the first job has finished, the second one will start. If JobDetail objects implement the Stateful interface, this won't happen. The second job will not start before the first one has finished. To make jobs resulting from the MethodInvokingJobDetailFactoryBean non-concurrent, set the concurrent flag to false.
緙虹渷鍦幫紝Quartz jobs鏄棤鐘舵佺殑錛屽湪jobs鐨勫彲鑳芥т綔涓虹粨鏋滃獎鍝嶅郊姝ゃ傚鏋滀綘闄愬畾涓や釜瑙﹀彂鍣ㄤ負鍚屼竴涓狫ohDetail,瀹冨湪絎竴涓猨ob宸茬粡瀹屾垚鏃舵槸鍙兘鐨勶紝絎簩涓皢浼氬紑濮嬨傚鏋淛obDetail瀹炵幇浜嗙姸鎬佹帴鍙o紝瀹冨皢涓嶄細鍙戠敓銆?BR><bean id="methodInvokingJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"><ref bean="exampleBusinessObject"/></property>
<property name="targetMethod"><value>doIt</value></property>
<property name="concurrent"><value>false</value></property>
</bean>
Note: By default, jobs will run in a concurrent fashion.
<bean id="methodInvokingJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"><ref bean="financeDAO"/></property>
<property name="targetMethod"><value>confirmOrder</value></property>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="methodInvokingJobDetail"/>
</property>
<property name="cronExpression">
<value>0 0 6,12,20 * * ?</value>
</property>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list><ref local="cronTrigger"/></list>
</property>
</bean>
涓婇潰榪欐閰嶇疆鏂囦歡瑙勫畾浜嗗湪鏃╀笂6鐐瑰拰鏅氫笂8鐐規墽琛宖inanceDAO瀵硅薄鐨刢onfirmOrder()鏂規硶.
闄勶細cronExpression閰嶇疆璇存槑
瀛楁 | 鍏佽鍊?/TH> | 鍏佽鐨勭壒孌婂瓧絎?/TH> | ||
---|---|---|---|---|
縐?/CODE> |
0-59 |
, - * / | ||
鍒?/CODE> |
0-59 |
, - * / | ||
灝忔椂 |
0-23 |
, - * / | ||
鏃ユ湡 |
1-31 |
, - * ? / L W C | ||
鏈堜喚 |
1-12 鎴栬?JAN-DEC |
, - * / | ||
鏄熸湡 |
1-7 鎴栬?SUN-SAT |
, - * ? / L C # | ||
騫達紙鍙夛級 |
鐣欑┖, 1970-2099 |
, - * / |
Trigger-related events include: trigger firings,trigger mis-firings(discussed in the "Triggers" sections of this document),and trigger completions (the jobs fired off by the trigger is finished).
To create a listener,simply create an object the implements either the org.quartz.TriggerListener and/or org.quartz.JobListener interface. Listeners are then registered with the scheduler during run time ,and must be given a name(or rather ,they must advertise their own name via their getName()method. Listeners can be registered as either "global" or "non-global". Global listeners receive events for ALL triggers/jobs, and non-global listeners receive events only for the specific triggers/jobs that explicitely name the listener in their getTriggerListenerNames() or getJobListenerNames() properties.
scheduler.addGlobalJobListener(myJobListener);
or
scheudler.addJobListener(myJobListener);
Listeners are not used by most users of Quartz,but are handy when application requirements create the need for the notification of events,without the Job itself explicitly nofifying the application.
CronTriggers are often more useful than SimpleTrigger, if you need a job-firing schedule that recurs based on calendar-like notions, rather than on the exactly specified intervals of SimpleTrigger.
CronTriggers 姣擲impleTrigger緇忓父鏇村姞鏈夌敤,濡傛灉浣犻渶瑕佷竴涓熀浜庡儚鏃ュ巻姒傚康鐨勯噸澶?job-firing 璋冨害錛岃屼笉鏄湪涓涓猄impleTrigger鐗瑰畾鐨勯棿闅斻?/P>
With CronTrigger, you can specify firing-schedules such as "every Friday at noon", or "every weekday and 9:30 am", or even "every 5 minutes between 9:00 am and 10:00 am on every Monday, Wednesday and Friday".
浣跨敤CronTrigger,浣犲彲浠ラ檺瀹歠iring-schedulers渚嬪 鈥滄瘡澶╀腑鍗堚滐紝鎴栬呪濓紝鈥濇瘡澶╁懆鏃ヤ笂鍗?:30鈥滐紝鎴栬呯敋鑷?鈥滄瘡5鍒嗛挓鍦ㄤ笂鍗?錛?0 鍒?10錛?0 姣忓懆涓銆佸懆涓夈佸懆浜斺?/P>
Cron Expressions
Cron 琛ㄨ揪寮?/P>
Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions are strings that are actually made up of six sub-expressions, that describe individual details of the schedule. These sub-expression are separated with white-space, and represent:
Cron 琛ㄨ揪寮忚鐢ㄦ潵娉ㄥ唽CronTrigger瀹炰緥鐨勩侰ron琛ㄨ揪寮忔槸瀛楃涓詫紝瀹冪敱鍏釜瀛愯〃杈懼紡緇勬垚錛屽畠鎻忚堪浜嗕笉鍚岀殑璋冨害緇嗚妭銆傝繖浜涘瓙琛ㄨ揪寮忚鐧借壊琛ㄨ揪寮忛殧寮錛岃〃鐜幫細
Seconds 縐?BR>Minutes 鍒?BR>Hours 鏃?BR>Day-of-Month 鏃?BR>Month 鏈?BR>Day-of-Week 鍛?/P>
An example of a complete cron-expression is the string "0 0 12 ? * WED" - which means "every Wednesday at 12:00 pm".
涓涓畬鏁寸殑Cron 琛ㄨ揪寮忎緥瀛愭槸瀛楃涓測? 0 12 ? * WEB鈥?鎰忓懗鐫姣忓懆涓変笂鍗?2錛?0銆?/P>
Individual sub-expressions can contain ranges and/or lists. For example, the day of week field in the previous (which reads "WED") example could be replaces with "MON-FRI", "MON, WED, FRI", or even "MON-WED,SAT".
鍗曠嫭鐨勫瓙琛ㄨ揪寮忓彲浠ュ寘鍚鉤琛岀殑 鍜?鎴栥備緥濡傦紝鍦ㄤ笂涓涓緥瀛愪竴鍛ㄧ殑涓澶╁瓧孌碉紙瀹冭浣?WED"錛夊彲浠ヨ鈥淢ON-FRI鈥?"MON,WED,FRI"錛屾垨鑰呯敋鑷?MON-WED,SAT"鏇挎崲鎺夈?/P>
Wild-cards (the '*' character) can be used to say "every" possible value of this field. Therefore the '*' character in the "Month" field of the previous example simply means "every month". A '*' in the Day-Of-Week field would obviously mean "every day of the week".
緇熼厤絎︼紙"*"瀛楃錛夊彲浠ヨ鐢ㄦ潵浣滀負榪欎釜瀛楁鐨?姣忎竴涓?鍙兘鍊箋傛墍浠?鍦ㄤ笂涓涓緥瀛愭湀瀛楁涓殑"*"瀛楃琛ㄧず姣忎釜鏈堛?涓涓?*"鍦ㄥ懆澶╁皢鏄庢樉鎰忓懗鐫鍛ㄧ殑姣忎竴澶┿?/P>
All of the fields have a set of valid values that can be specified. These values should be fairly obvious - such as the numbers 0 to 59 for seconds and minutes, and the values 0 to 23 for hours. Day-of-Month can be any value 0-31, but you need to be careful about how many days are in a given month! Months can be specified as values between 0 and 11, or by using the strings JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC. Days-of-Week can be specified as vaules between 1 and 7 (1 = Sunday) or by using the strings SUN, MON, TUE, WED, THU, FRI and SAT.
鎵鏈夊瓧孌甸兘鐢ㄤ竴涓悎娉曢檺瀹氱殑鍊箋傝繖浜涘煎簲璇ユ槸鏄庢樉鐨勶紝渚嬪0鍒?9鏁板瓧涓虹鍜屽垎鐨勯檺瀹氾紝0鍒?3涓哄皬鏃躲傛湀鐨勬煇澶╁彲浠ユ槸0-31鐨勶紝鎴栬呬綘闇瑕佹秷鎭粰涓湀鏈夊灝戝ぉ錛佹湀浠藉彲浠ヨ闄愬畾鍦?鍒?1錛屾垨鑰咃紝浣跨敤鑻辨枃瀛楃涓茬緝鍐欍備竴涓ぜ鎷滅殑涓澶╁彲浠ヨ闄愬畾浣滀負1鍒?錛?=Sunnday錛夋垨鑰呬嬌鐢ㄨ嫳鏂囧瓧絎︿覆銆?/P>
The '/' character can be used to specify increments to values. For example, if you put '0/15' in the Minutes field, it means 'every 15 minutes, starting at minute zero'. If you used '3/20' in the Minutes field, it would mean 'every 20 minutes during the hour, starting at minute three' - or in other words it is the same as specifying '3,23,43' in the Minutes field.
"/"瀛楃鍙互鍐呯敤鏉ラ檺瀹氬肩殑澧炲姞銆備緥濡傦紝濡傛灉浣犲皢'0/15'鏀懼埌鍒嗛挓瀛楁錛屽畠鎰忓懗鐫"姣?5鍒嗛挓錛屽紑濮嬩簬0鍒嗛挓"銆傚鏋滀綘浣跨敤"3/20"鍦ㄥ垎閽熷瓧孌典腑錛屼綘灝嗘剰鍛崇潃"涓涓皬鏃跺唴姣?0鍒嗛挓錛屽紑濮嬩簬3鍒嗛挓"--- 鎴栬呮崲璦涔嬶紝瀹冨拰鍦ㄥ垎閽熷瓧孌?3,23,43"闄愬畾鏄竴鏍風殑銆?/P>
The '?' character is allowed for the day-of-month and day-of-week fields. It is used to specify "no specific value". This is useful when you need to specify something in one of the two fields, but not the other. See the examples below (and CronTrigger JavaDOC) for clarification.
"?"瀛楃鏄厑璁鎬負鏈堢殑鏌愪竴澶╂垨鑰呭懆鐨勬煇涓澶╁瓧孌電殑銆傚畠琚敤鏉ラ檺瀹?娌℃湁闄愬畾鍊?銆傝繖鏄湁鐢ㄧ殑錛屽綋浣犻渶瑕侀檺瀹氫竴浜涗簨鎯呭湪涓涓垨涓や釜瀛楁涓紝浣嗕笉鏄繖閲岀殑銆?/P>
The 'L' character is allowed for the day-of-month and day-of-week fields. This character is short-hand for "last", but it has different meaning in each of the two fields. For example, the value "L" in the day-of-month field means "the last day of the month" - day 31 for January, day 28 for February on non-leap years. If used in the day-of-week field by itself, it simply means "7" or "SAT". But if used in the day-of-week field after another value, it means "the last xxx day of the month" - for example "6L" or "FRIL" both mean "the last friday of the month". When using the 'L' option, it is important not to specify lists, or ranges of values, as you'll get confusing results.
"L"瀛楃鏄厑璁哥敤鏉ユ湀鏌愬ぉ鍜屽懆鏌愬ぉ瀛楁銆傝繖涓瓧絎︽槸涓涓?last"鐨勭緝鍐欙紝浣嗘槸瀹冩湁涓嶅悓鐨勬剰涔夊湪涓や釜瀛楁鐨勫叾涓箣涓銆備緥濡傦紝榪欎釜鍊?L"鍦ㄦ湀瀛楁鐨勬煇涓澶╂剰鍛崇潃" 榪欎釜鏈堢殑鏈鍚庝竴澶?錛?1鎴栬?8絳夌瓑銆?/P>
Here are a few more examples of expressions and their meanings - you can find even more in the JavaDOC for CronTrigger
CronTrigger Example 1 - an expression to create a trigger that simply fires every 5 minutes
"0 0/5 * * * ?"
CronTrigger Example 2 - an expression to create a trigger that fires every 5 minutes, at 10 seconds after the minute (i.e. 10:00:10 am, 10:05:10 am, etc.).
"10 0/5 * * * ?"
CronTrigger Example 3 - an expression to create a trigger that fires at 10:30, 11:30, 12:30, and 13:30, on every Wednesday and Friday.
"0 30 10-13 ? * WED,FRI"
CronTrigger Example 4 - an expression to create a trigger that fires every half hour between the hours of 8 am and 10 am on the 5th and 20th of every month. Note that the trigger will NOT fire at 10:00 am, just at 8:00, 8:30, 9:00 and 9:30
"0 0/30 8-9 5,20 * ?"
Note that some scheduling requirements are too complicated to express with a single trigger - such as "every 5 minutes between 9:00 am and 10:00 am, and every 20 minutes between 1:00 pm and 10:00 pm". The solution in this scenario is to simply create two triggers, and register both of them to run the same job.
Quartz 鏄竴涓姛鑳介綈鍏ㄧ殑銆佸紑婧愮殑job鏃墮棿璋冨害緋葷粺錛屽畠鍙互琚粨鍚堢殑,鎴栬呬即闅忚櫄鎷熺殑浠諱綍J2EE鎴朖2SE紼嬪簭-浠庢渶灝忕殑鐙珛鐨勫簲鐢ㄧ▼搴忓埌鏈澶х殑e-commerce緋葷粺琚嬌鐢ㄣ俀uartz鍙互鐢ㄦ潵鍒涘緩綆鍗曟垨澶嶆潅鐨勬椂闂磋皟搴︽潵鎵ц鍗併佺櫨銆佸崈銆佺敋鑷充笂涓囩殑jobs;鑾峰緱鐨刯ob琚畾涔変負涓涓爣鍑嗙殑java緇勪歡鎴朎JBs. 榪欎釜Quartz 鏃墮棿璋冨害鍖呭惈寰堝浼佷笟綾葷殑鐗瑰緛錛屼緥濡侸TA浜嬪姟鍜岀皣銆?/P>
The licensing of Quartz versions 1.0 through 1.4.5 is similar to both the BSD and ASF (Apache) public licenses, which means it's free for use, even within commercial products.
Quartz 1.0鐗堟湰鍒?.4.5鐗堟湰鐨刲icensing 鏄被浼間笌BSD鍜孉SF(apache)鍏叡licenses, 瀹冩剰鍛崇潃鍙互鍏嶈垂浣跨敤錛岀敋鑷充嬌鐢ㄥ湪鍟嗕笟浜у搧涓嬌鐢ㄣ?BR>With the upcoming release of version 1.5.0, Quartz is moving to the Apache 2.0 license.
浼撮殢1.5.0鐗堟湰鐨勫彂甯冿紝Quartz灝嗚漿縐誨悜apache 2.0 鐨刲icense.
What can Quartz do for you? (Quartz鍙互涓轟綘鍋氫粈涔堬紵)
If your application has tasks that need to occur at given moments in time, or if your system has recurring maintenance jobs then Quartz may be your ideal solution.
濡傛灉浣犵殑紼嬪簭鏈変竴浜涜繖鏍風殑浠誨姟錛屽畠闇瑕佸強鏃跺湴鍙戠敓鍦ㄧ粰瀹氭椂闂達紝鎴栬呬綘濡傛灉浣犵殑緋葷粺鏈夎繛緇淮鎶obs錛岄偅涔圦uartz鍙互鎴愪負浣犵殑鐞嗘兂鐨勮В鍐蟲柟妗堛?/P>
Sample uses of job scheduling with Quartz:
Quartz浣跨敤job鏃墮棿璋冨害鐨勮寖渚?/P>
Driving Workflow: As a new order is initially placed, schedule a Job to fire in exactly 2 hours, that will check the status of that order, and trigger a warning notification if an order confirmation message has not yet been received for the order, as well as changing the order's status to 'awaiting intervention'.
System Maintenance: Schedule a job to dump the contents of a database into an XML file every business day (all weekdays except holidays) at 11:30 PM.
宸ヤ綔嫻侀┍鍔細浣滀負涓涓柊鐨勮鍗曡鍒濆鍖栨斁緗紝璋冨害涓涓猨ob鍘誨伐浣滃湪姝eソ涓や釜灝忔椂鍐咃紝瀹冨皢媯鏌ヨ鍗曠殑鐘舵侊紝騫朵笖瑙﹀彂涓涓鍛婇氱煡濡傛灉璁㈠崟紜淇℃伅娌℃湁琚帴鏀訛紝鍙堟敼鍙樿鍗曠殑鐘舵佸埌"絳夊緟騫叉秹"銆?BR>緋葷粺緇存姢錛氳皟搴︿竴涓猨ob鏉ュ皢鏁版嵁搴撹漿鍖栦負XML鏂囦歡 姣忓晢涓氭棩鏈燂紙鎵鏈夊懆鏈櫎浜嗚妭鍋囨棩錛夊湪涓嬪崍11錛?0銆?/P>
The basic way to create an AOP proxy in Spring to use the org.springframework.aop.framework.ProxyFactoryBean. This gives complete control over the pointcuts an advice that will apply, and theire ordering . However ,there are simpler options that are preferable(鏇村彲鍙栫殑銆佹洿濂界殑) if you don't need such control.
Basics
The proxyFactoryBean,like other Spring FactoryBean implementations,introduces a level of indirection(闂存帴). If you define a ProxyFactoryBean with name foo,what objects referencing foo see is not the ProxyFactoryBean instance itself, but an object created by the ProxyFactoryBeans's implementation of the getObject() method. This method will create an AOP proxy wrapping a target object.
One of the most important benefits of using a ProxyFactoryBean or other IoC-aware to create AOP proxies, it that it means that advices and pointcuts can also be managed by IoC. This is a powerful feature , enabling certain approaches that are hard to achieve with other AOP frameworks. For example,an advice may itself reference application objects(besides the target , which should be available in any AOP framework),benefiting from all the pluggability provided by Dependency Injection.
JavaBean properties
Like most FactoryBean implementations provided with Spring, ProxyfactoryBean is itself a JavaBean. It properties are used to:
Specify the target you want to proxy
Specify whether to use CGLIB
Some key properties are inherited from org.springframework.aop.framework.ProxyConfig: the subclass for all AOP proxy factories. These include:
proxyTargetClass: true if we should proxy the target class,rather than its interfaces. If this is true we need to use CGLIB.
optimize: whether to apply aggressive optimization to created proxies. Don't use this setting unless you understand how the relevant(鐩稿叧鐨? AOP proxy handles optimization. This is currently used only for CGLIB proxies;it has no effect with JDK dynamic proxies(the default).
frozen:whether avice changes should be disallowed once the proxy factory has bean configured. Default is false.
exposeProxy: whether the current proxy should be exposed in a ThreadLocal so that it can be accessed by the target (It's available via the MethodInvocation without the need for a ThreadLocal) If a target needs to obtain the proxy and exposedProxy is true, the target can use the AopContext.currentProxy() method.
aopProxyFactory: the implementation of AopProxyFactory to use. Offers a way of customizing whether to use dynammic proxies,CGLIB or any other proxy strategy. The default implementation will choose dynamic proxies or CGLIB appropriately. There should be need to use this property, it's intended to allow the addition of new proxy types in spring 1.1.
Other properties specific to ProxyFactoryBean include:
.proxyInterfaces: array of String interface names. If this isn't supplied, a CGLIB proxy for the target class will be used.
.interceptorNames:String array of Advisor,interceptor or other advice names to apply.Ordering is sugnicicant. first come,first serve that is. The first interceptor in the list will be the first to be able to interceptor the invocation (of course if it concerns a regular MethodInterceptor or BeforeAdvice. The names are bean names in the current factory , including bean names from ancestor factories. You can't mention bean references here since doing so would result iin the ProxyFactoryBean ignoring the singleton setting of the advise. you can append an iinterceptor name with an asterisk(*). This will result in the application of all advisor beans withe names starting with the part before the asterisk to be applied. An example of using this feature can be found below.
Singleton: whether or not the factory should return a single object, no matter how often the getObject() method is called. Server FactoryBean implementations offer such a method. Default value is true. If you want to use stateful advice --for example ,for stateful mixins-user prototype advices along withe s singleton value of false.
Proxying interfaces
<bean id="personTarget" class="com.mycompany.PersionImpl">
<property name="name"><value>Tony</value></property>
<property name="age"><value>51</value></property>
</bean>
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty"><value>Custom string property value</value></property>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInteceptor" ></bean>
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterface"><value>com.company.Person</value></property>
<property name="target"><ref local="personTarget"/></property>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
13.7.1. Dependencies 錛堜緷璧栵級
Your application will need to include the latest release of JasperReports, which at the time of writing was 0.6.1. JasperReports itself depends on the following projects:
BeanShell
Commons BeanUtils
Commons Collections
Commons Digester
Commons Logging
iText
POI
JasperReports also requires a JAXP compliant(閫傚簲鐨? XML parser.
13.7.2. Configuration錛堥厤緗級
To configure JasperReports views in your ApplicationContext you have to define a ViewResolver to map view names to the appropriate view class depending on which format you want your report rendered in.
13.7.2.1. Configuring the ViewResolver
Typically, you will use the ResourceBundleViewResolver to map view names to view classes and files in a properties file
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>views</value>
</property>
</bean>
Here we've configured an instance of ResourceBundleViewResolver which will look for view mappings in the resource bundle with base name views. The exact contents of this file is described in the next section.
13.7.2.2. Configuring the Views
Spring contains five different View implementations for JasperReports four of which corresponds to one of the four output formats supported by JasperReports and one that allows for the format to be determined at runtime:
JasperReport View Class
1.JasperReportsView CSV
2.JasperReportsHtmlView HTML
3.JasperReportsPdfView PDF
4.JasperReportsXlsView EXCEL
5.JasperReportsMutiFormatView
Mapping one of these classes to a view name and a report file is simply a matter of adding the appropriate entries into the resource bundle configured in the previous section as shown here:
simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
Here you can see that the view with name, simpleReport, is mapped to the JasperReportsPdfView class. This will cause the output of this report to be rendered in PDF format. The url property of the view is set to the location of the underlying report file.
13.7.2.3. About Report Files
JasperReports has two distinct types of report file: the design file, which has a .jrxml extension, and the compiled report file, which has a .jasper extension. Typically, you use the JasperReports Ant task to compile your .jrxml design file into a .jasper file before deploying it into your application. With Spring you can map either of these files to your report file and Spring will take care of compiling the .jrxml file on the fly for you. You should note that after a .jrxml file is compiled by Spring, the compiled report is cached for the life of the application. To make changes to the file you will need to restart your application.
JasperReports鎷ユ湁涓ょ涓嶅悓鐨勭被鍨嬬殑鎶ヨ〃鏂囦歡錛氳璁℃枃浠訛紝瀹冩槸涓涓嫢鏈?jrxml 鎵╁睍鐨勬枃浠訛紝鍜岀紪璇戝ソ鐨勬姤琛ㄦ枃浠躲備竴鑸紝浣犱嬌鐢╝nt浠誨姟鍦ㄤ綘閮ㄧ講鍒頒綘鐨勭▼搴忎腑涔嬪墠鏉ョ紪璇戜綘鐨?jrxml璁捐鎶ヨ〃鏂囦歡銆備嬌鐢⊿pring 浣犲彲浠ュ獎灝勮繖浜涙枃浠朵腑鐨勪換涓鍒頒綘鐨勬姤琛ㄦ枃浠訛紝Spring灝嗕細涓轟綘鍦ㄧ┖闂叉椂鐓ч【緙栬瘧.jrxml鏂囦歡銆?浣犲簲褰撴敞鎰忓湪涓涓?jrxml鏂囦歡琚紪璇戜箣鍚庯紝榪欎釜緙栬瘧鐨勬姤琛ㄦ槸琚紦瀛樼殑鍦ㄤ綘鐨刟pplication鐢熷懡鍛ㄦ湡涓傚鏋滆繖浜涙枃浠朵慨鏀逛簡錛屼綘闇瑕侀噸鏂板惎鍔ㄧ殑浣犵殑紼嬪簭銆?/P>
13.7.2.4. Using JasperReportsMultiFormatView 浣跨敤JasperReportsMutiFormatView
The JasperReportsMultiFormatView allows for report format to be specified at runtime. The actual rendering of the report is delegated to one of the other JasperReports view classes - the JasperReportsMultiFormatView class simply adds a wrapper layer that allows for the exact implementation to be specified at runtime.
JasperReportsMutilFormatView鍏佽浣犲湪榪愯鏃舵湡鎸囧畾鎶ヨ〃鐨勬牸寮忋傛姤琛ㄧ殑瀹為檯鐨勮〃鐜版槸涓哄鎵樺埌JasperReports 瑙嗗浘綾葷殑涓殑涓涓?-JasperMutilFormatView綾葷畝鍗曠殑鍔犱簡涓涓寘瑁呭眰鍏佽鍦ㄨ繍琛屾椂鏈熸紜殑瀹炵幇琚寚瀹氥?/P>
The JasperReportsMultiFormatView class introduces two concepts: the format key and the discriminator key. The JasperReportsMultiFormatView class uses the mapping key to lookup the actual view implementation class and uses the format key to lookup up the mapping key. From a coding perspective you add an entry to your model with the formay key as the key and the mapping key as the value, for example:
public ModelAndView handleSimpleReportMulti(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String uri = request.getRequestURI();
String format = uri.substring(uri.lastIndexOf(".") + 1);
Map model = getModel();
model.put("format", format);
return new ModelAndView("simpleReportMulti", model);
}
In this example, the mapping key is determined from the extension of the request URI and is added to the model under the default format key: format. If you wish to use a different format key then you can configure this using the formatKey property of the JasperReportsMultiFormatView class.
By default the following mapping key mappings are configured in JasperReportsMultiFormatView:
Table 13.3. JasperReportsMultiFormatView Default Mapping Key Mappings
Mapping Key View Class
csv JasperReportsCsvView
html JasperReportsHtmlView
pdf JasperReportsPdfView
xls JasperReportsXlsView
So in the example above a request to URI /foo/myReport.pdf would be mapped to the JasperReportsPdfView class. You can override the mapping key to view class mappings using the formatMappings property of JasperReportsMultiFormatView.
When using RMI, it's not possible to access the objects through the HTTP protocol, unless you're tunneling the RMI traffic. RMI is a fairly heavy-weight protocol in that it support full-object serialization which is important when using a complex data model that needs serialization over the wire. However, RMI-JRMP is tied to Java clients: It is a Java-to-Java remoting solution.
褰撴垜浠嬌鐢≧MI錛屾垜浠笉鍙互閫氳繃http鍗忚璁塊棶瀵硅薄錛岄櫎闈炰綘鎵撻歊MI浜ら氱殑闅ч亾銆俁MI 鏄竴涓潪甯擱噸閲忕駭鍗忚錛屽湪鍏朵腑浠栨敮鎸佺殑鎵鏈夊璞$殑搴忓垪鍖栨槸闈炲父閲嶈鐨勶紝褰撲嬌鐢ㄤ竴涓渶瑕佸簭鍒楀寲鍏寵仈鐨勫鏉傜殑鏁版嵁妯″瀷銆傜劧鑰岋紝RMI-JRMP 鏄緷璧杍ava瀹㈡埛绔殑錛氬畠鏄痡ava-to-java鐨勮繙紼嬭В鍐蟲柟妗堛?/P>
Spring's HTTP invoker is a good choice if you need HTTP-based remoting but also rely on Java serialization. It shares the basic infrastructure with RMI invokers, just using HTTP as transport. Note that HTTP invokers are not only limited to Java-to-Java remoting but also to Spring on both the client and server side. (The latter also applies to Spring's RMI invoker for non-RMI interfaces.)
Spring 鐨凥TTP invoker 鏄竴涓笉閿欑殑閫夋嫨錛屽鏋滀綘闇瑕佸熀浜嶩TTP鐨勮繙紼嬶紝鑰屼笖闇瑕乯ava搴忓垪鍖栧洖澶嶃傚畠浣跨敤RMI invoker鍒嗕韓浜嗗熀紜緇撴瀯錛屼粎浠呬嬌鐢℉TTP浣滀負浼犺緭銆傛敞鎰廐TTP invoker涓嶆槸浠呬粎闄愬埗緇檍ava-to-java鐨勮繙紼嬭屼笖鏄湪瀹㈡埛绔拰鏈嶅姟鍣ㄧ鐨凷pring.(鍚庨潰鐨勪篃搴旂敤鍒癝pring 鐨凴MI invoker涓洪潪RMI鐨勬帴鍙?銆?/P>
Hessian and/or Burlap might provide significant value when operating in a heterogeneous environment, because they explicitly allow for non-Java clients. However, non-Java support is still limited. Known problems include the serialization of Hibernate objects in combination with lazily initializing collections. If you have such a data model, consider using RMI or HTTP invokers instead of Hessian.
Hessian 鍜屾垨 Burlap鍙互鏀寔閲嶈鐨勫鹼紝褰撳湪涓涓紓璐ㄧ殑鐜鎿嶄綔錛屽洜涓轟粬浠槑紜殑鍏佽涓洪潪java 瀵硅薄銆傜劧鑰岄潪java瀵硅薄鏄彈闄愬埗鐨勶紝鐭ラ亾鐨勯棶棰樺寘鎷琱ibernate瀵硅薄搴忓垪鍖栦笌鎳掓眽鍒濆鍖栭泦鍚堢殑緇撳悎涓娿傚鏋滀綘鏈夎繖鏍風殑鏁版嵁妯″瀷錛岃冭檻浣跨敤RMI鎴朒TTP invokers鑰屼笉鏄疕essioan.
JMS can be useful for providing clusters of services and allowing the JMS broker to take care of load balancing, discovery and auto-failover. By default Java serialization is used when using JMS remoting but the JMS provider could use a different mechanism for the wire formatting, such as XStream to allow servers to be implemented in other technologies.
Last but not least, EJB has an advantage over RMI in that it supports standard role-based authentication and authorization and remote transaction propagation. It is possible to get RMI invokers or HTTP invokers to support security context propagation as well, although this is not provided by core Spring: There are just appropriate hooks for plugging in third-party or custom solutions here.