代码:spring-annotation
扫描的时候,如果是自定义扫描规则,需要写一个实现类 实现 TypeFilter接口
1、添加spring框架的依赖
maven的pom.xml文件中,添加spring框架、spring-aspect、spring-jdbc、junit、数据池c3p0,mysql驱动等依赖(依赖都去对应的官网找)
说明:除了spring相关的依赖,其他组件可以自己随意添加
2、Spring配置类注解版(替代原来的XML配置文件)
2.1 项目结构
spring-annotation | src | main | java | com.atguigu | aop | |
bean | ||||||
condition | ||||||
config | 配置类,可以写多个配置类替代配置文件 | |||||
controller | ||||||
dao | ||||||
ext | ||||||
service | ||||||
tx | 事务 | |||||
MainTest.java | main函数 | |||||
resources | beans.xml | spring配置文件(个别配置类不好写的) | ||||
dbconfig.properties | ||||||
person.properties | ||||||
test | java | com.atguigu.test | 一系列测试类 | |||
resources | 一般为空 |
说明:配置类可以写多个配置,以前配置文件的所有配置都可以在配置类里面写。一定要会查官网。举例不完的
2.2 配置类一:开启一般扫描组件
package com.atguigu.config;
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
@ComponentScans(
value = {
@ComponentScan(value="com.atguigu",includeFilters = {
/* @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
}
)
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
(1)@Configuration:类级别注释,只能放在类上面,告诉spring这是一个配置类
(2)@ComponentScans:扫描项目里面的文件夹,告诉spring将哪些文件夹下的类添加进spring容器
说明:可以在配置类中用@Bean等注解将组件添加进容器,也可以直接在组件上添加注解,让其进入容器。直接在组件上添加注解让其进入容器的情况一般是单实例的时候,多实例的时候,在组件上直接标注解处理很麻烦。
需要搭配@Filter
@Filter的Type有5种,见官网https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-annotation-config
默认是annotation
@ComponentScans(
value = {
@ComponentScan(value="com.atguigu",includeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
}
)//@ComponentScan value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则,使用自定义规则,就要写代码了import org.springframework.core.type.filter.TypeFilter; public class MyTypeFilter implements TypeFilter { /** * metadataReader:读取到的当前正在扫描的类的信息 * metadataReaderFactory:可以获取到其他任何类信息的 */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // TODO Auto-generated method stub //获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类资源(类的路径) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->"+className); if(className.contains("er")){ return true; } return false; } }
你也可以通过在注释上设置useDefaultFilters=false来禁用默认过滤器,或者通过提供use-default-filters="false"作为<component-scan/>元素。这有效地禁用了使用@Component、@Repository、@Service、@Controller、@RestController或@Configuration注释或元注释的类的自动检测。
2.3 配置类2—按条件扫描组件、导入外部组件(外部组件可以不用加@Bean等注解了)
package com.atguigu.config;
//满足WindowsCondition.class的条件,这个类中配置的所有bean注册才能生效;
@Conditional({WindowsCondition.class})
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
//@Import导入组件,id默认是组件的全类名
public class MainConfig2 {
@Scope("prototype") //开启多实例模式
@Lazy//单实例bean:默认在容器启动的时候创建对象,开启器懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
@Bean("person")
public Person person(){
System.out.println("给容器中添加Person....");
return new Person("张三", 25);
}
/**
* @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
*
* 如果系统是windows,给容器中注册("bill")
* 如果是linux系统,给容器中注册("linus")
*/
@Bean("bill")
public Person person01(){
return new Person("Bill Gates",62);
}
@Conditional(LinuxCondition.class) //如果是linux系统才添加linux
@Bean("linus")
public Person person02(){
return new Person("linus", 48);
}
@Bean \\工厂bean,把真实的colorbean隐藏,返回一个工厂bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
(1)@Conditional({WindowsCondition.class}) ,
//类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
可以放在类 和 方法上 。放在类上,类里面所有返回的bean都要接受判断,包括@import来的外部bean
现在需要去WindowsCondition.class这个类写条件代码(就是实现Condition接口)
关于ConditionContext类和 AnnotatedTypeMetadata就看看官方文档吧package com.atguigu.condition; //判断是否windows系统 public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if(property.contains("Windows")){ return true; } return false; } }
(2)@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@Import导入组件,id默认是组件的全类名—ImportSelector:返回需要导入的组件的全类名数组;(需要写代码,实现ImportSelector,返回数组)
—ImportBeanDefinitionRegistrar:手动注册bean到容器中 (需要写代码实现ImportBeanDefinitionRegistrar)package com.atguigu.condition; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有需要添加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red"); boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue"); if(definition && definition2){ //指定Bean定义信息;(Bean的类型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } } } //自定义逻辑返回需要导入的组件 public class MyImportSelector implements ImportSelector { //返回值,就是到导入到容器中的组件全类名 //AnnotationMetadata:当前标注@Import注解的类的所有注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // TODO Auto-generated method stub //importingClassMetadata //方法不要返回null值 return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"}; } }
注意:当心和扫描包@ComponentScan冲突
(3)创建对象配置(不用xml的bean标签了)
在创建对象的方法上写
@Scope("prototype"):调整作用域,默认就是单实例。如果不想改变可以不用写该注解
官网1.5 Bean作用域https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes
prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
* 每次获取的时候才会调用方法创建对象;
* singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
* 以后每次获取就是直接从容器(map.get())中拿,
* request(web有效):同一次请求创建一个实例
* session(web有效):同一个session创建一个实例@Lazy 懒加载:
* 单实例bean:默认在容器启动的时候创建对象;
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;@Bean("Bill"),将该注解标注的方法返回的对象添加进容器,即导入的第三方包里面的组件
扩展:工厂bean
使用Spring提供的 FactoryBean(工厂Bean);
* 1)、默认获取到的是工厂bean调用getObject创建的对象
* 2)、要获取工厂Bean本身,我们需要给id前面加一个&
* &colorFactoryBean案例最后一个bean就是工厂bean,工厂bean本体需要实现factoryBean接口,重写3个方法
1、返回什么的真实bean
2、类本身
3、是否单例
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}bean本体代码:
package com.atguigu.bean; //创建一个Spring定义的FactoryBean public class ColorFactoryBean implements FactoryBean<Color> { //返回一个Color对象,这个对象会添加到容器中 @Override public Color getObject() throws Exception { // TODO Auto-generated method stub System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class<?> getObjectType() { // TODO Auto-generated method stub return Color.class; } //是单例? //true:这个bean是单实例,在容器中保存一份 //false:多实例,每次获取都会创建一个新的bean; @Override public boolean isSingleton() { // TODO Auto-generated method stub return false; } }
扩展1:给类创建的对象赋值,只要赋值好,配置类里面用无参构造器就可以获得这个赋值好的bean组件
Person类:
package com.atguigu.bean;
public class Person {
//使用@Value赋值;
//1、基本数值
//2、可以写SpEL; #{}
//3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
@Value("张三")
private String name;
@Value("#{20-2}") \\20-2=18
private Integer age;
@Value("${person.nickName}")\\从person.properties文件中取出keynikeName的值
private String nickName;
//get、set、toString
}
说明:
/使用@Value赋值;
//1、基本数据类型包含String,不能赋值对象
//2、可以写SpEL; #{} 备注:参考我前面的JSP里面的EL表达式
//3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)@PropertySource("classpath:/person.properties") 给出配置文件所在的目录,加载外部配置文件
特别说明:以上案例 Person开启了多实例模式,无参构造器才直接拿Person实例里面的@Value的值,下面的配置类就创建了一个person实例,从配置文件取值
package com.atguigu.config; //使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值 @PropertySource(value={"classpath:/person.properties"}) @Configuration public class MainConfigOfPropertyValues { @Bean public Person person(){ return new Person(); } }
测试:
package com.atguigu.test;
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
printBeans(applicationContext);
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);
//工厂Bean获取的是调用getObject创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型:"+bean2.getClass());
System.out.println(bean2 == bean3);
//这里就是colorFactoryBean本体啦
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
}
@Test
public void test02(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
System.out.println("ioc容器创建完成....");
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
System.out.println(bean == bean2);
}
@SuppressWarnings("resource")
@Test
public void test01(){
//new也不会创建新的对象,只是获取那个对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
//以下测试配置文件创建的person对象
public class IOCTest_PropertyValue {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
@Test
public void test01(){
printBeans(applicationContext);
System.out.println("=============");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
applicationContext.close();
}
//打印容器中的所有bean
private void printBeans(AnnotationConfigApplicationContext applicationContext){
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
2.3 配置类— Bean生命周期
package com.atguigu.config;
import com.atguigu.bean.Car;
@ComponentScan("com.atguigu.bean")
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod="init",destroyMethod="detory")
public Car car(){
return new Car();
}
}
实体类car
package com.atguigu.bean;
@Component
public class Car {
public Car(){
System.out.println("car constructor...");
}
public void init(){
System.out.println("car ... init...");
}
public void detory(){
System.out.println("car ... detory...");
}
}
结合https://blog.csdn.net/chengqingshihuishui/article/details/112383046 的生命周期可以再看一次
bean的生命周期:
* bean创建---初始化----销毁的过程
* 容器管理bean的生命周期;
* 我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
*
* 构造(对象创建)
* 单实例:在容器启动的时候创建对象
* 多实例:在每次获取的时候创建对象\
*
* 后置处理器:(即BeanPostProcessor接口实现类),可以让开发期间,在初始化方法前后,加入一些程序员自己的逻辑代码BeanPostProcessor.postProcessBeforeInitialization
* 初始化:
* 对象创建完成,并赋值好,调用初始化方法。。。
* BeanPostProcessor.postProcessAfterInitialization
* 销毁:
* 单实例:容器关闭的时候
* 多实例:容器不会管理这个bean;容器不会调用销毁方法;
* 遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
* 一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitializationpackage com.atguigu.bean; /** * 后置处理器:初始化前后进行处理工作 * 将后置处理器加入到容器中 * @author lfy */ @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean); return bean; } }
总结:
1)、指定初始化和销毁方法;
* 通过@Bean指定init-method和destroy-method;
* 2)、通过让Bean实现InitializingBean(定义初始化逻辑),
* DisposableBean(定义销毁逻辑);
* 3)、可以使用JSR250;
* @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
* @PreDestroy:在容器销毁bean之前通知我们进行清理工作
* 4)、BeanPostProcessor【interface】:bean的后置处理器;
* 在bean初始化前后进行一些处理工作;
* postProcessBeforeInitialization:在初始化之前工作
* postProcessAfterInitialization:在初始化之后工作
*
* Spring底层对 BeanPostProcessor 的使用;
* bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
测试:
package com.atguigu.test;
public class IOCTest_LifeCycle {
@Test
public void test01(){
//1、创建ioc容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成...");
//applicationContext.getBean("car");
//关闭容器
applicationContext.close();
}
}
2.4 Bean组件的装配@Autowired
也可以结合前面的Spring教程来看
step1. bean实体里面写一个BookDao(就是要将这个bookdao通过@Autowired自动注入进别的类BookService)
package com.atguigu.dao;
import org.springframework.stereotype.Repository;
//名字默认是类名首字母小写
@Repository
public class BookDao {
private String lable = "1";
public String getLable() {
return lable;
}
public void setLable(String lable) {
this.lable = lable;
}
@Override
public String toString() {
return "BookDao [lable=" + lable + "]";
}
}
step2. 编写BookService类,将BookDao自动注入进来(注入的是第一个对象)
package com.atguigu.service;
@Service
public class BookService {
//@Qualifier("bookDao")
//@Autowired(required=false)
//@Resource(name="bookDao2")
@Inject//注入标记了@primary的Bookdao
private BookDao bookDao;
public void print(){
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService [bookDao=" + bookDao + "]";
}
}
step3.编写配置类,再配置类里面在创建一个bookDao类的对象bookDao2,并添加进容器。这下bookDao就有两个对象了
package com.atguigu.config;
@Configuration
@ComponentScan({"com.atguigu.service","com.atguigu.dao",
"com.atguigu.controller","com.atguigu.bean"})
public class MainConifgOfAutowired {
@Primary//有两个bookDao组件,后面有组件要注入这个的话,首选这个
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
说明:貌似使用了@Primary就自动开启多实例模式了(@Scope)
自动装配;
* Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
*
* 1)、@Autowired:自动注入:
* 1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
* 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
* applicationContext.getBean("bookDao")
* 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
* 4)、自动装配默认一定要将属性赋值好,没有就会报错;
* 可以使用@Autowired(required=false);
* 5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
* 也可以继续使用@Qualifier指定需要装配的bean的名字
* BookService{
* @Autowired
* BookDao bookDao;
* }
*
* 2)、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
* @Resource:
* 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
* 没有能支持@Primary功能没有支持@Autowired(reqiured=false);
* @Inject:
* 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
* @Autowired:Spring定义的; @Resource、@Inject都是java规范
*
* AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;
*
* 3)、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
* 1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
* 2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
* 3)、放在参数位置:
*
* 4)、补充知识点:自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
* 自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
* 把Spring底层一些组件注入到自定义的Bean中;
* xxxAware:功能使用xxxProcessor;
* ApplicationContextAware==》ApplicationContextAwareProcessor;package com.atguigu.bean; @Component public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // TODO Auto-generated method stub System.out.println("传入的ioc:"+applicationContext); this.applicationContext = applicationContext; } @Override public void setBeanName(String name) { // TODO Auto-generated method stub System.out.println("当前bean的名字:"+name); } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { // TODO Auto-generated method stub String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}"); System.out.println("解析的字符串:"+resolveStringValue); } }
说明:通过重写方法里面的入参获取到spring的底层组件,然后又赋值给自己编写类的私有属性。这样这个类里面的所有方法都可以用到了
*
step4 测试
package com.atguigu.test;
public class IOCTest_Autowired {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class);
BookService bookService = applicationContext.getBean(BookService.class);
System.out.println(bookService);
//BookDao bean = applicationContext.getBean(BookDao.class);
//System.out.println(bean);
Boss boss = applicationContext.getBean(Boss.class);
System.out.println(boss);
Car car = applicationContext.getBean(Car.class);
System.out.println(car);
Color color = applicationContext.getBean(Color.class);
System.out.println(color);
System.out.println(applicationContext);
applicationContext.close();
}
}
2.5 配置环境—profile
* Profile:
* Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
*
* 开发环境、测试环境、生产环境;
* 数据源:(/A)(/B)(/C);
* @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
*
* 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
* 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
* 3)、没有标注环境标识的bean在任何环境下都是加载的;
复习:
补充知识点:自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
* 自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
* 把Spring底层一些组件注入到自定义的Bean中;
* xxxAware:功能使用xxxProcessor;
* ApplicationContextAware==》ApplicationContextAwareProcessor;
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}")
private String user;
//通过最后的重写方法获取spring底层组件valueResolver,并赋值过来
private StringValueResolver valueResolver;
//用valueResolver解析了SPEL表达式然后又赋值过来
private String driverClass;
@Bean
public Yellow yellow(){
return new Yellow();
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override \\该方法来设置私有属性reslover,并且马上用于解析SPEL
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}
dbconfig.propertis文件
spring-annotation\src\main\resources\dbconfig.propertis(显然,该文件在类路径下)
db.user=root
db.password=123456
db.driverClass=com.mysql.jdbc.Driver
说明:前面在@value给属性赋值的时候,SPEL表达式都是直接用的,没想到写代码的时候,SPEL表达式还需要spring底层的解析器来解析了。
以上配置了三个不同环境,开发环境、测试环境、生产环境,连接的数据库不同,其他账号密码都一样。
spring可以快速激活环境,最简单的办法,命令行模式切换环境
(1)命令行模式切换环境
profile
激活某个环境,需要通过IDEA虚拟机模拟 右键某个测试方法,run-configuration
命令 :
第二种方式,代码方式切换环境
package com.atguigu.test;
public class IOCTest_Profile {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
//1、创建一个applicationContext
//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//3、注册主配置类,这个类里面的Bean可以获取了
applicationContext.register(MainConfigOfProfile.class);
//4、启动刷新容器
applicationContext.refresh();
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}
Yellow bean = applicationContext.getBean(Yellow.class);
System.out.println(bean);
applicationContext.close();
}
}
3、配置AOP 动态代理(结合https://blog.csdn.net/chengqingshihuishui/article/details/112528712一起学习)
AOP指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
通知方法:
* 前置通知(@Before):logStart:在目标方法(div)运行之前运行
* 后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
* 返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
* 异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
* 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
package com.atguigu.config;
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
//业务逻辑类加入容器中
@Bean
public MathCalculator calculator(){
return new MathCalculator();
}
//切面类加入到容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
* 在Spring中很多的 @EnableXXX;配置类给容器添加了2个组件
一个业务逻辑类(MathCalculator)对象;在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
package com.atguigu.aop; public class MathCalculator { public int div(int i,int j){ System.out.println("MathCalculator...div..."); return i/j; } }
一个日志切面类(LogAspects)对象:切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
package com.atguigu.aop; /** * 切面类 * @author lfy * * @Aspect: 告诉Spring当前类是一个切面类 * */ @Aspect public class LogAspects { //抽取公共的切入点表达式 //1、本类引用 //2、其他的切面引用 @Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))") public void pointCut(){}; //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入) @Before("pointCut()") public void logStart(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}"); } @After("com.atguigu.aop.LogAspects.pointCut()") public void logEnd(JoinPoint joinPoint){ System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After"); } //JoinPoint一定要出现在参数表的第一位 @AfterReturning(value="pointCut()",returning="result") public void logReturn(JoinPoint joinPoint,Object result){ System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}"); } @AfterThrowing(value="pointCut()",throwing="exception") public void logException(JoinPoint joinPoint,Exception exception){ System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}"); } }
说明:returnning指的是 MathCalculator类里面需要被增强的方法的返回值,throwing也是被增强方法发生的异常()
正常执行:前置通知-》目标方法-》后置通知-》返回通知
出现异常:前置通知-》目标方法-》后置通知-》异常通知
*JoinPoint接口:
getArgs()
: Returns the method arguments.
getThis()
: Returns the proxy object.
getTarget()
: Returns the target object.
getSignature()
: Returns a description of the method that is being advised.
toString()
: Prints a useful description of the method being advised.
AOP测试:
package com.atguigu.test;
public class IOCTest_AOP {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
//1、不要自己创建对象,去容器拿对象
// MathCalculator mathCalculator = new MathCalculator();
// mathCalculator.div(1, 1);
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
mathCalculator.div(1, 0); //这个就会抛异常了
applicationContext.close();
}
}
4、配置—Spring事务
不晓得为啥,这门课把事务的配置,专门放在了另一个包
* 声明式事务:
*
* 环境搭建:
* 1、导入相关依赖
* 数据源、数据库驱动、Spring-jdbc模块
* 2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
* 3、给方法上标注 @Transactional 表示当前方法是一个事务方法;
* 4、 @EnableTransactionManagement 开启基于注解的事务管理功能;
* @EnableXXX
* 5、配置事务管理器来控制事务;
* @Bean
* public PlatformTransactionManager transactionManager()
@EnableTransactionManagement
@ComponentScan("com.atguigu.tx")
@Configuration
public class TxConfig {
//数据源
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
//
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
编写好实体类:
package com.atguigu.tx;
//Dao 层
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(){
String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)";
String username = UUID.randomUUID().toString().substring(0, 5);
jdbcTemplate.update(sql, username,19);
}
}
//Service层,注入Dao
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void insertUser(){
userDao.insert();
//otherDao.other();xxx
System.out.println("插入完成...");
int i = 10/0;
}
}
测试:
package com.atguigu.test;
public class IOCTest_Tx {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
applicationContext.close();
}
}
5、事件发布与监听
5.1 事件发布
5.1.1 标准事件,自动触发事件
遇到事情,会自动发布的事件(Spring提供的标准事件)
以上事件发生时,就会通知负责监听的Bean组件,开发者就可以在Bean组件里写点东西。
当然,除了有上面自动触发的事件外,还可以自定义事件,并发布
5.1.2 发布自定义事件 https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#context-functionality-events
官网方法:
(1)编写一个事件类,继承ApplicationEvent ,或者就直接用ApplicationEvent的构造函数,创建一个事件(测试类就是用的这招)
exp:官网案例:反正就是自己创造一个事件
public class BlockedListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
(2)发布事件
通过实现ApplicationEventPublisherAware接口,拿到里面的ApplicationEventPublisher类的对象publisher组件,这个组件的publishEvent方法,就可以发布事件了
官网代码:
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
//这里获取到publisher,官网没有标记,估计是个重写方法
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content)); //发布事件
return;
}
// send email...
}
}
5.2 监听容器中发布的事件
以上事件,不管是spring标准事件,还是自定义事件,我们监听到以后。都可以写一些代码来响应
两种方法写监听器
1)实现public interface ApplicationListener<E extends ApplicationEvent>接口的实现类
package com.atguigu.ext;
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
//当容器中发布此事件以后,方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:"+event);
}
}
2)@EventListener 标注的方法
package com.atguigu.ext;
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class}) //官网有多个类的写法
public void listen(ApplicationEvent event){
System.out.println("UserService。。监听到的事件:"+event);
}
}
ApplicationListener:监听容器中发布的事件。事件驱动模型开发;
* public interface ApplicationListener<E extends ApplicationEvent>
* 监听 ApplicationEvent 及其下面的子事件;
*
* 步骤:
* 1)、写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
* 或者再某个类上标注@EventListener的方法
* 原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener;
*
* 2)、把监听器加入到容器;
* 3)、只要容器中有相关事件的发布,我们就能监听到这个事件;
* ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
* ContextClosedEvent:关闭容器会发布这个事件;
* 4)、发布一个事件://这个比官方那个方法快捷多了,待会测试,就用这个方法发布自定义事件
* applicationContext.publishEvent();
*
底层事件相关的三个组件:ContextRefreshedEvent、IOCTest_Ext$1[source=我发布的事件]、ContextClosedEvent;自动触发事件的时机:
* 1)、ContextRefreshedEvent事件:
* 1)、容器创建对象:refresh();
* 2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
* 2)、自己发布事件;
* 3)、容器关闭会发布ContextClosedEvent;
*
5.3 测试
package com.atguigu.test;
public class IOCTest_Ext {
@Test
public void test01(){
//创建好bean就会触发标准事件
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
//发布事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
});
//关闭的时候也会触发标准事件,监听器都会听到的
applicationContext.close();
}
}
springsource 源码总结
======总结===========
1)、Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;
1)、xml注册bean;<bean>
2)、注解注册Bean;@Service、@Component、@Bean、xxx
2)、Spring容器会合适的时机创建这些Bean
1)、用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;
2)、统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();
3)、后置处理器;BeanPostProcessor
1)、每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
AutowiredAnnotationBeanPostProcessor:处理自动注入
AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
xxx....
增强的功能注解:
AsyncAnnotationBeanPostProcessor
....
4)、事件驱动模型;
ApplicationListener;事件监听;
ApplicationEventMulticaster;事件派发:
6、Servlet3.0+SpringMvc注解版整合+Spring
6.1 配置类配置WEB三大组件
了解就好,这年代,谁还用Servlet写代码啊
Servlet3.0也不需要写xml配置文件了,直接写类+注解
1、Servlet容器启动会扫描,当前应用里面每一个jar包的
ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;
必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件的内容就是ServletContainerInitializer实现类的全类名;总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet.ServletContainerInitializer
指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;
注册三大组件:Servlet、listener、filter
package com.atguigu.servlet;
//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class}) //这个HelloService类是最基础的接口,他的子类都是各个配置类组件
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 应用启动的时候,会运行onStartup方法;
*
* Set<Class<?>> arg0:已经写好的配置类组件
* ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
*
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
* 必须在项目启动的时候来添加;
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*/
@Override
public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
// TODO Auto-generated method stub
System.out.println("感兴趣的类型:");
for (Class<?> claz : arg0) {
System.out.println(claz);
}
//注册组件 ServletRegistration
ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
//配置servlet的映射信息
servlet.addMapping("/user");
//注册Listener
sc.addListener(UserListener.class);
//注册Filter FilterRegistration
FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
//配置Filter的映射信息
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
三大组件分别为:
(1)UserServlet
package com.atguigu.servlet;
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
resp.getWriter().write("tomcat...");
}
}
(2)UserListener
package com.atguigu.servlet;
/**
* 监听项目的启动和停止
* @author lfy
*
*/
public class UserListener implements ServletContextListener {
//监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
System.out.println("UserListener...contextDestroyed...");
}
//监听ServletContext启动初始化
@Override
public void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generated method stub
ServletContext servletContext = arg0.getServletContext();
System.out.println("UserListener...contextInitialized...");
}
}
(3)UserFilter
package com.atguigu.servlet;
public class UserFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
// 过滤请求
System.out.println("UserFilter...doFilter...");
//放行
arg2.doFilter(arg0, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
6.2 Servlet3.0整合SpringMvc
6.2.1 配置好相关依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu</groupId>
<artifactId>springmvc-annotation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
6.2.2 整合spring和springmvc和Servlet
重点:整合说明
1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
说明:该步骤是前文的Servlet3.0目录下的代码,上例中这个文件指定Servlet自己的类,这个案例,就需要指定springmvc的类了
2、加载这个文件指定的类SpringServletContainerInitializer
3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
创建一个web的ioc容器;createServletApplicationContext();
创建了DispatcherServlet;createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中;
getServletMappings();
3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
创建根容器:createRootApplicationContext()
getRootConfigClasses();传入一个配置类
创建web的ioc容器: createServletApplicationContext();
获取配置类;getServletConfigClasses();
总结:
以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息;
(1)启动Web的MVC容器
package com.atguigu;
//web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//获取根容器的配置类;(Spring的配置文件)父容器
@Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[]{RootConfig.class};
}
//获取web容器的配置类(SpringMVC配置文件) 子容器;
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[]{AppConfig.class};
}
//获取DispatcherServlet的映射信息
// /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
// /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[]{"/"};
}
}
(2)编写启动类里面所需要的配置文件 (就是填充上文启动文件里面的空)
1)spring配置文件:
package com.atguigu.config;
//Spring的容器不扫描controller;父容器
@ComponentScan(value="com.atguigu",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
public class RootConfig {
}
spring作为最外层大管家,扫描除了controller以外的所有组件
2)springmvc配置
定制SpringMVC;
1)、@EnableWebMvc:开启SpringMVC定制配置功能;
<mvc:annotation-driven/>;
2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
extends WebMvcConfigurerAdapter
3)、只扫描controller,controller就只标注web相关的bean
package com.atguigu.config;
//SpringMVC只扫描Controller;子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.atguigu",includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)
@EnableWebMvc //SpringMVC的高级定制功能开启(可加载mvc部分注解) --><mvc:annotation-driven />
public class AppConfig extends WebMvcConfigurerAdapter {
//定制
//视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// TODO Auto-generated method stub
//默认所有的页面都从 /WEB-INF/ xxx .jsp
//registry.jsp();
registry.jsp("/WEB-INF/views/", ".jsp");
}
//静态资源访问,原xml文件中的<mvc:default-servlet-handler/>
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// TODO Auto-generated method stub
configurer.enable();
}
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TODO Auto-generated method stub
//super.addInterceptors(registry);
registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
}
}
4)编写上面的拦截器
实现 HandlerInterceptor接口
package com.atguigu.controller;
public class MyFirstInterceptor implements HandlerInterceptor {
//目标方法运行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
System.out.println("preHandle..."+request.getRequestURI());
return true;
}
//目标方法执行正确以后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("postHandle...");
}
//页面响应以后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("afterCompletion...");
}
}
5)然后可以写controller
和该controller相关的service和jsp就不演示了,和springmvc普通版一样的
package com.atguigu.controller;
@Controller
public class HelloController {
@Autowired
HelloService helloService;
@ResponseBody
@RequestMapping("/hello")
public String hello(){
String hello = helloService.sayHello("tomcat..");
return hello;
}
// /WEB-INF/views/success.jsp
@RequestMapping("/suc")
public String success(){
return "success";
}
}
6.2.3 异步处理
(1)servlet来处理异步请求
package com.atguigu.servlet;
@WebServlet(value="/async",asyncSupported=true)
public class HelloAsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、支持异步处理asyncSupported=true
//2、开启异步模式
System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
AsyncContext startAsync = req.startAsync();
//3、业务逻辑进行异步处理;开始异步处理
startAsync.start(new Runnable() {
@Override
public void run() {
try {
System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
sayHello();
startAsync.complete();
//获取到异步上下文
AsyncContext asyncContext = req.getAsyncContext();
//4、获取响应
ServletResponse response = asyncContext.getResponse();
response.getWriter().write("hello async...");
System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
} catch (Exception e) {
}
}
});
System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
}
public void sayHello() throws Exception{ //写给上面方法的函数调用的
System.out.println(Thread.currentThread()+" processing...");
Thread.sleep(3000);
}
}
(2)springmvc来处理异步请求 controller
callable接口,请看多线程https://blog.csdn.net/chengqingshihuishui/article/details/109191707
package com.atguigu.controller;
//创建订单超时,就失败
@Controller
public class AsyncController {
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder(){
DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");
DeferredResultQueue.save(deferredResult);
return deferredResult; //service里面的对象,这里就不写了
}
@ResponseBody
@RequestMapping("/create")
public String create(){
//创建订单
String order = UUID.randomUUID().toString();
DeferredResult<Object> deferredResult = DeferredResultQueue.get();
deferredResult.setResult(order);
return "success===>"+order;
}
/**
* 1、控制器返回Callable
* 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
* 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
* 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
* 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
*
* preHandle.../springmvc-annotation/async01
主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
=========DispatcherServlet及所有的Filter退出线程============================
================等待Callable执行==========
副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
================Callable执行完成==========
================再次收到之前重发过来的请求========
preHandle.../springmvc-annotation/async01
postHandle...(Callable的之前的返回值就是目标方法的返回值)
afterCompletion...
异步的拦截器:
1)、原生API的AsyncListener
2)、SpringMVC:实现AsyncHandlerInterceptor;
* @return
*/
@ResponseBody
@RequestMapping("/async01")
public Callable<String> async01(){
System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
//这里直接new了一个callable接口的实现类
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return "Callable<String> async01()";
}
};
System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return callable;
}
}
说明:以上request首先到async01 方法,