告诉spring这是一个配置类,对应于application.xml文件
通过AnnotationConfigApplicationContext来加载配置类
Spring对@Configuration类会特殊处理;给容器中加组件的方法,第一次是创建,之后的调用都是从容器中找组件
//使用application.xml时使用ClassPathXmlApplicationContext加载容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person bean = (Person) applicationContext.getBean("person");
//使用@Configuration时使用AnnotationConfigApplicationContext加载容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
用于配置类中,给容器注册一个bean,类型为返回值的类型,id默认是用方法名作为id,对应于application.xml中的
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
写在配置类上,配置注解扫描,value为需要扫描的包路径(是重复注解)
默认扫描配置的路径下的**@Controller**、@Service、@Repository,@Component
@Controller、@Service、@Repository,@Component注册的bean的名字默认是类名小写
属性 excludeFilters = Filter[],指定扫描时排除那些组件
属性 includeFilters = Filter[],指定扫描的时候只需要包含哪些组件,需要先设置useDefaultFilters = false
@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 {
...
}
写在配置类上面 快速给容器中导入一个组件
@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
ImportSelector:返回需要导入的组件的全类名数组(springboot中经常使用)
ImportBeanDefinitionRegistrar:手动注册bean到容器中
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
//@Import导入组件,id默认是组件的全类名
public class MainConfig2 {
...
}
工厂Bean接口
默认获取到的是工厂bean调用getObject创建的对象
要获取工厂Bean本身,我们需要给id前面加一个&,即&colorFactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
//获取FactoryBean
Object bean = applicationContext.getBean("&colorFactoryBean");
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog....@PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("Dog....@PreDestroy...");
}
Spring底层中 BeanPostProcessor 后置处理器 ,用于bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx
postProcessBeforeInitialization 在初始化之前工作
遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
postProcessAfterInitialization 在初始化之后工作
常用的有:
ApplicationContextAwareProcessor: 实现了在bean初始化时将ioc容器注入bean中
BeanValidationPostProcessor :可以在bean创建完成时对数据进行校验
InitDestroyAnnotationBeanPostProcessor 处理@PostConstruct和@PreDestroy
AutowiredAnnotationBeanPostProcessor 在对象创建完之后处理@Autowired标注的所有属性
基本数值
可以写SpEL; #{}
可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值),需要结合@PropertySource注解使用
@Value("张三")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.nickName}")
private String nickName;
(可重复注解)读取配置文件 value传String数组,可以导入多个配置文件
<context:property-placeholder location="classpath:person.properties"/>
// 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;
// 加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person(){
return new Person();
}
}
// 所设置的配置文件中的值也能通过IOC容易取到
// 文件中的所有值都会被加载到ConfigurableEnvironment中
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
自动装配:Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值
可以用于构造器,方法,属性,参数上
默认优先按照类型去容器中找对应的组件
applicationContext.getBean(BookDao.class);找到就赋值
如果找到多个相同类型的组件
则将属性的名称作为组件的id去容器中查找 applicationContext.getBean(“bookDao”)
自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false),这样没有找到Bean就不会装配
[标注在方法上]:
标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;方法使用的参数,自定义类型的值从ioc容器中获取
@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
[标注在构造器上]:
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,构造器要用的组件,都是从容器中获取
[标注在参数位置]
参数位置的组件还是可以自动从容器中获取
使用@Qualifier指定需要装配的组件的id,而不是使用属性名
让Spring进行自动装配的时候,默认使用首选的bean(未指定时使用首选)
也可以继续使用@Qualifier指定需要装配的bean的名字(指定后就按指定的装备)
可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
不支持@Primary,不支持@Autowired(reqiured=false);
需要导入javax.inject的包,和Autowired的功能一样。支持@Primary,不支持required=false;
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
把Spring底层一些组件注入到自定义的Bean中;
xxxAware:功能使用xxxProcessor;
ApplicationContextAware==》ApplicationContextAwareProcessor;
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。
默认是default环境写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
没有标注环境标识的bean在任何环境下都是加载的;
切换环境
1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
2、代码的方式激活
//设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//启动刷新容器
applicationContext.refresh();
AOP:指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
在目标方法(div)运行之前运行
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
...
}
在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束
@After("com.atguigu.aop.LogAspects.pointCut()")
在目标方法(div)正常返回之后运行
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
...
}
在目标方法(div)出现异常以后运行
@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
...
}
//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
public void pointCut(){};
告诉Spring哪个类是切面类
@Aspect
public class LogAspects {
...
}
加在配置类上,开启基于注解的aop模式
@Import(AspectJAutoProxyRegistrar.class):导入AspectJAutoProxyRegistrar
AspectJAutoProxyRegistrar用于自定义给容器中注册bean;
给容器中注册一个AnnotationAwareAspectJAutoProxyCreator(一个后置处理器);
AnnotationAwareAspectJAutoProxyCreator 实现了SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
@EnableAspectJAutoProxy 开启AOP功能
@EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
容器的创建流程:
是:将切面的通知方法,包装成增强器(Advisor);给 业务逻辑组件(目标对象)创建一个代理对象(cglib);代理对象里面包含所有的增强器
执行目标方法:
正常执行:前置通知-》目标方法-》后置通知-》返回通知
出现异常:前置通知-》目标方法-》后置通知-》异常通知
开启基于注解的事务管理功能,同时需要配置事务管理器才会生效
配置事务管理器
//注册事务管理器在容器中 传入数据源
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
利用@Import(TransactionManagementConfigurationSelector.class)给容器中会导入组件
导入两个组件
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;
InfrastructureAdvisorAutoProxyCreator:
利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
事务拦截器:
先获取事务相关的属性
再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
最终会从容器中按照类型获取一个PlatformTransactionManager;
执行目标方法
- 如果异常,获取到事务管理器,利用事务管理回滚操作
- 如果正常,利用事务管理器,提交事务
就是IOC容器或对象工厂
所有的bean都是由BeanFactory(也就是IOC容器)来进行管理的 。如XMLBeanFactory就是一种典型的BeanFactory。常用的ApplicationContext接口也是由BeanFactory接口派生而来,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先 。(管理所有的Bean)
參考1.5 是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。(本身是Bean,但是能够生产Bean)
public interface FactoryBean<T> {
//返回的对象实例
T getObject() throws Exception;
//Bean的类型
Class<?> getObjectType();
//true是单例,false是非单例 在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回true
boolean isSingleton();
}
bean后置处理器,是Spring中定义的接口,在Spring容器 的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。
public interface BeanPostProcessor {
//bean初始化前调用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化后调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
beanFactory的后置处理器
invokeBeanFactoryPostProcessors(beanFactory)
在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容;
时机:所有的bean定义已经加载到beanFactory中,但是bean的实例还未创建,*在**初始化创建其他组件前面执行
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
extends BeanFactoryPostProcessor
包含postProcessBeanDefinitionRegistry()方法
优先于BeanFactoryPostProcessor执行;
BeanDefinitionRegistryPostProcessor可以给容器中再额外添加一些组件;
时机:所有bean定义信息将要被加载,bean实例还未创建
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
// BeanDefinitionRegistry Bean定义信息的保存中心,
// 以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
//方法1.继承自BeanFactoryPostProcessor,在方法2后执行
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
...
}
@Override
//方法2.继承自BeanDefinitionRegistryPostProcessor,先执行
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//可以在postProcessBeanDefinitionRegistry方法中自己动态添加bean到容器中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();
registry.registerBeanDefinition("hello", beanDefinition);
}
}
功能:监听容器中发布的事件(ApplicationEvent),完成事件驱动模型开发
//ApplicationEvent 事件
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
//当容器中发布此事件以后,此方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("收到事件:"+event);
}
}
//发布事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我的事件")) {
});
@EventListener 让任意一个bean也能实现监听事件
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("UserService。。监听到的事件:"+event);
}
}
SmartInitializingSingleton
public interface SmartInitializingSingleton {
/**
*时机:所有的单实例Bean创建完之后会触发该方法
*/
void afterSingletonsInstantiated();
}
容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet**.ServletContainerInitializer**
指定的实现类,启动并运行这个实现类的方法,传入感兴趣的类型;
使用ServletContext注册Web组件(Servlet、Filter、Listener)
使用编码的方式,在项目启动的时候给ServletContext里面添加组件,必须在项目启动的时候来添加
@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 应用启动的时候,会运行onStartup方法;
*
* Set> arg0:感兴趣的类型的所有子类型;
* ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个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, "/*");
}
}
@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);
}
}
web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
加载这个文件指定的类SpringServletContainerInitializer
spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
并且为WebApplicationInitializer组件创建对象(条件:组件不是接口,不是抽象类)
创建一个web的ioc容器;createServletApplicationContext();
创建了DispatcherServlet;createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中;
getServletMappings();
AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
创建根容器:createRootApplicationContext()
getRootConfigClasses();传入一个配置类
创建web的ioc容器: createServletApplicationContext()
获取配置类;getServletConfigClasses();
//web容器启动的时候创建对象;调用方法来初始化容器以及前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//获取根容器的配置类;(Spring的配置文件) 父容器;
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class};
}
//获取web容器的配置类(SpringMVC配置文件) 子容器;
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{AppConfig.class};
}
//获取DispatcherServlet(前端控制器)的映射信息
// /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
// /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//Spring的容器不扫描controller 父容器
@ComponentScan(value="com.test",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
public class RootConfig {
}
<mvc:annotation-driven />
//SpringMVC只扫描Controller 子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.test",includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
//定制
//视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
//静态资源访问
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
}
}
/*
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...
*/
@ResponseBody
@RequestMapping("/async01")
public Callable<String> async01(){
System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
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 async01()" ;
}
};
System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return callable;
}
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder(){
DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");
//创建订单但是不处理 临时保存
DeferredResultQueue.save(deferredResult);
return deferredResult;
}
@ResponseBody
@RequestMapping("/create")
public String create(){
//创建订单 ,处理订单
String order = UUID.randomUUID().toString();
DeferredResult<Object> deferredResult = DeferredResultQueue.get();
deferredResult.setResult(order);//处理完成
return "success===>"+order;
}
//模拟一个队列
public class DeferredResultQueue {
private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
public static void save(DeferredResult<Object> deferredResult){
queue.add(deferredResult);
}
public static DeferredResult<Object> get( ){
return queue.poll();
}
}