1.Profile
profile在位不同环境下使用不同的配置提供了支持(如开发环境和生成环境的数据库配置)
使用方式
a.通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境,在开发中使用@Profile注解在类或者方法上,达到不同情况下选择实例化不同的bean。
b.通过设定jvm的spring.profiles.active参数来设置配置环境。
c.Web项目设置在Servlet的context parameter中。
Servlet2.5及以下
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>spring.active.profile</param-name>
<param-value>production</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Servlet 3.0之后
public class ProfileConfig implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setAttribute("spring.profiles.default", "dev");
}
测试
第一步:我们创建类,通过@Profile注解来标记在什么环境下返回什么样的bean
@Configuration
@ComponentScan("com.pingan.haofang.profile")
public class ProfileConfig {
@Bean
@Profile("dev")
public ProfileService getDevService(){
return new ProfileService("dev");
}
@Bean
@Profile("prod")
public ProfileService getProdService(){
return new ProfileService("Prod");
}
}
public class ProfileService {
private String content;
public ProfileService(String content) {
this.content = content;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
第二步:测试,使用@ActiveProfiles("dev")标明使用的环境
junit测试:
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
@ContextConfiguration(classes = ProfileConfig.class) //加载配置文件
@ActiveProfiles("dev")
public class ProfileTest {
@Autowired
private ProfileService profileService;
@Before
public void beforeTest() {
System.out.println("start");
}
@Test
public void doTest() {
String content = profileService.getContent();
System.out.println(content);
}
}
main方法测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");//设置活动的Profiles为dev
context.register(ProfileConfig.class);//注册配置类
context.refresh();//刷新容器
ProfileService service = context.getBean(ProfileService.class);
System.out.println(service.getContent());
}
2.spring事件(Application Event)
在开发工作中,会遇到一种场景,做完某一件事情以后,需要广播一些消息或者通知,告诉其他的模块进行一些事件处理,一般来说,可以一个一个发送请求去通知,但是有一种更好的方式,那就是事件监听,事件监听也是设计模式中 发布-订阅模式、观察者模式的一种实现。
spring的事件为bean与bean的消息通知提供了支持,当一个bean处理完任务时,希望另外一个bean能够知道并做出相应的处理,这时我们就需要让另外一个bean监听当前bean所发送的事件
观察者模式:简单的来讲就是你在做事情的时候身边有人在盯着你,当你做的某一件事情是旁边观察的人感兴趣的事情的时候,他会根据这个事情做一些其他的事,但是盯着你看的人必须要到你这里来登记,否则你无法通知到他(或者说他没有资格来盯着你做事情)。
要想顺利的创建监听器,并起作用,这个过程中需要这样几个角色:
1、事件(event):可以封装和传递监听器中要处理的参数(构造方法传值),如对象或字符串,并作为监听器中监听的目标。
2、监听器(listener):具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象参数。业务处理在监听器中
3、事件发布者(publisher)事件发生的触发者。
spring事件的流程
a.自定义事件,继承ApplicaitonEvent,重写构造方法
b.自定义事件监听器,实现ApplicationListener(一种方式),获取事件中封装的参数(getSource()),做业务处理
c.使用容器发布事件,发布事件时指定事件的参数。
通过案例测试一下
第一步,自定义事件,继承ApplicationEvent
public class PvEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
public PvEvent(Object source) {
super(source);
}
}
第二步:自定义事件监听器,实现ApplicationListener,指定事件监听的类型(监听到事件后做插入数据)
监听器的实现方式1:实现ApplicationListener
@Component
public class PvListener implements ApplicationListener<PvEvent> {
@Autowired
private BaseDao baseDao;
@Override
public void onApplicationEvent(PvEvent event) {
String id = (String) event.getSource();
//根据这个id做新增,更新等其他操作
JobDto job =new JobDto();
job.setDesc("id");
job.setCronExpression("0 0/3 * * * ? ");
job.setJobClassName("com.pingan.yisheng.quartz.test.JobTest");
job.setJobGroup("myGroup");
job.setJobName("myName");
job.setJobType("2");
baseDao.saveAndFlush(job);
}
//监听器的实现方式2:可以在一个class中定义多个方法,用@EventListener来做方法级别的注解
@Component
public class MyAnnotationListener {
@EventListener
public void listener1(MyTestEvent event) {
System.out.println("注解监听器1:" + event.getMsg());
}
@EventListener
@Async //异步执行逻辑,需要指定executor
public void listener2(MyTestEvent event) {
System.out.println("注解监听器2:" + event.getMsg());
}
}
此时,就可以有一个发布,两个监听器监听到发布的消息了,一个是注解方式,一个是非注解方式
并且注解形式的监听器的执行走在了非注解的前面
第三步:使用容器发布事件(注入ApplicationContext用于发布事件)
@Component
public class PvPublisher //implements ApplicationEventPublisherAware 发布事件方式3 {
@Autowired
ApplicationContext applicationContext;//发布事件方式1
//@Autowired
//ApplicationEventPublisher publisher;//发布事件方式2
public void publish() {
//publisher.publishEvent(new PvEvent("id"));
applicationContext.publishEvent(new PvEvent("id"));
}
// @Override
// public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
// applicationEventPublisher.publishEvent(new PvEvent("id"));
//
// }
}
测试:
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
//@ContextConfiguration(locations={"classpath:applicationContext.xml"}) //加载配置文件
@ContextConfiguration(classes = PvConfig.class)
public class SpringEventTest {
@Autowired
PvPublisher publisher;
@Before
public void beforeTest() {
System.out.println("start");
//AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PvConfig.class);
}
@Test
public void doTest() {
publisher.publish();//事件发布时被事件监听器监听,执行监听器中方法
System.out.println(1234321);
}
在实际工作中,事件监听经常会用在发送通知,消息、邮件等情况下,那么这个时候往往是需要异步执行的,不能在业务的主线程里面,那怎么样可以实现异步处理呢?当然你可以写一个线程,单独做这个事情,在此,我比较推荐的是用spring的@Async注解方式,一个简单的注解,就可以把某一个方法或者类下面的所有方法全部变成异步处理的方法,这样,就可以做到处理监听事件的时候也不会阻塞主进程了。
使用@Async注解需要指定一个executor供其使用,根据@Async(value ="executorId")指定executor的id
如果指定executor的2种方式:
xml配置
<task:annotation-driven executor="asyncExecutor" />
<task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10"/>
pool-size:
core size:最小的线程数,缺省:1
max size:最大的线程数,缺省:Integer.MAX_VALUE
queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE
keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉
rejection-policy:当pool已经达到max size的时候,如何处理新任务
ABORT(缺省):抛出TaskRejectedException异常,然后不执行
DISCARD:不执行,也不抛出异常
DISCARD_OLDEST:丢弃queue中最旧的那个任务
CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
使用配置类实现:
Configuration
@ComponentScan("com.pingan.haofang.taskexecutor")
@EnableAsync //开启异步任务支持
public class taskExecuterConfig implements AsyncConfigurer {
/*
* 配置并获取ThreadPoolTaskExecutor
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor excutor = new ThreadPoolTaskExecutor();
excutor.setCorePoolSize(10);
excutor.setMaxPoolSize(20);
excutor.setKeepAliveSeconds(0);
excutor.setQueueCapacity(50);
excutor.initialize();
return excutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// TODO Auto-generated method stub
return null;
}
}