Spring实践
factory_method 工厂创建bean实例
依赖倒置
A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。 B. 抽象不能依赖于具象,具象依赖于抽象。
- IOC控制反转:说的是创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是你在xml文件控制,侧重于原理。
- DI依赖注入:说的是创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。
配置
- @Component:用于说明一个类是一个spring容器管理的类。@Controller, @Service, @Repository是- @Component的细化,这三个注解比@Component带有更多的语义,它们分别对应了控制层、服务层、持久层的类。@Component注解或者其子类(找个本质上还是@Component)标记的类,认为这些类是bean, 并且把这些bean对应的beanDefinition放到容器中进行管理。BeanDefinition是对bean的描述,里边存有bean的名称,Class等基本信息。
- @Service:用于服务层的IServiceImpl类文件,
- @Controller:标识类是一个控制器。在servlet中使用。
- @Repository:写在类上,标识该类作为一个spring bean由spring进行管理,即可将其注入到其它类中去。给持久层的类定义一个名字,让Spring根据这个名字关联到这个类。
- @RequestMapping:可以注解在@Controller标注的类上,也可以注解@Controller标注类的方法,指定请求到方法的映射关系。为了进行更精确匹配,使用value指定路径,method指定请求方法,params指定请求参数。Header指定请求头。返回值的类型有约束。
- @ResponseBody:表示这个方法将直接响应HTTP内容。
- @Autowired:写在字段上,或写在setter方法上,用来装配bean。这里的bean就是前面几个标注定义的。相当于new了一个实现类,不需要实现setter方法。
- @Resource:作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面 @Resource 默认按 byName 自动注入罢了。
- @PathVariable:用于访问URI template变量。
对应xml脚本的配置,此时会探测base-package包下的注释的bean到spring容器中,当然这里的fitler也可以不要:
<context:component-scan base-package="com.zxr.manager">
<context:include-filter type="regex" expression=".*DaoImpl"/>
<context:include-filter type="regex" expression=".*ServiceImpl"/>
</context:component-scan>
在bean中配置的property,需要类中有对应的set实现方法,或者标签。
ClassPathXmlApplicationContext
指定classpath方式,具体使用如下,
- “classpath:” 用户加载类路径(包括jar包)中的一个且仅一个资源,对于对个匹配的也值返回一个。
- “classpath*:” 可以匹配对个资源,并返回。 classpath后面的路径中可以采用ant模式匹配,其中,
- ?:匹配一个字符。
- *:匹配零个或者多个字符,如cn/*/config.xml匹配cn/sub/config.xml,但是不匹配cn/config.xml.
- **:匹配路径中的零个或多个目录,如cn/**/config.xml匹配cn/sub/config.xml,也匹配cn/config.xml。 实际使用时,发现该匹配方式会有遗漏,建议使用全路径指定吧。
spring bean 作用域
singleton(默认),prototype,reqeust,session,global-session。
bean启动流程:
构造函数,setter,preprocessbeforeInit,init-method,postPrecessAfterInit,destroy-method。 自动装配:no,byname,bytype,构造函数,autodetect。
- 1,ApplicationContext接口,没有任何方法,只是继承了BeanFactory接口,暗示ApplicationContext与BeanFactory都是获取Bean的地方。
- 2,AbstractApplicationContext抽象类,首先,它的构造函数接收入参BeanFactory,所以说ApplicationContext内部具有一个BeanFactory。类似于一种装饰器模式,但不是装饰器模式,类似于代理模式,但也不是代理模式。fresh方法分为三个步骤:
- loadBeanDefinitions,这个是一个模板方法,需要子类实现,它的作用就是从某一个地方读取BeanDefinition,然后写入到ApplicationContext自己的BeanFactory里面,这就是ApplicationContext与BeanFactory之间的联系,也就是ApplicationContext还负责了读取定义。
- registerBeanPostProcessors,这个就是在BeanFactory里面找到BeanPostProcessor,然后将他们放到BeanFactory的beanPostProcessors容器里面,方便BeanFactory初始化使用。
- onRefresh初始化一遍所有的bean。
ClassPathXmlApplicationContext实现了loadBeanDefinitions的方法,将xml文件和BeanFactory结合在一起。 - 资源加载到BeanFactory,BeanFactory中PostProcessor的bean注册到BeanFactory;
- 遍历BeanFactory中所有bean的定义:根据BeanDefinition,创建实例(工厂方法,容器自动装配,无参构造函数),加入到BeanDefintion中;(单例,lazy_init为false)
- 3,执行BeanPostProcessor
- 如果有@PostConstruct注解,则调用之;(前置处理阶段,AOP)
- 将属性注入到Bean中,afterPropertiesSet;
- 初始化bean,init-method;
- 执行postProcessor修饰bean,并用返回的bean替换到当前bean;(后置处理阶段)
controller处理方法返回值的可选类型:
- ModelAndView
ModelAndView构造函数可以指定返回页面的名称,也可以通过setViewName方法来设置所需要跳转的页面; - Model
一个模型对象,主要包含spring封装好的model和modelMap,以及java.util.Map,当没有视图返回的时候视图名称将由requestToViewNameTranslator决定; - View
这个时候如果在渲染页面的过程中模型的话,就会给处理器方法定义一个模型参数,然后在方法体里面往模型中添加值。 - String
返回字符串表示一个视图名称,这个时候如果需要在渲染视图的过程中需要模型的话,就可以给处理器添加一个模型参数,然后在方法体往模型添加值就可以了, - Void
当返回类型为Void的时候,则响应的视图页面为对应着的访问地址:这个时候我们一般是将返回结果写在了HttpServletResponse 中了,如果没写的话,spring就会利用RequestToViewNameTranslator 来返回一个对应的视图名称。如果这个时候需要模型的话,处理方法和返回字符串的情况是相同的。
事务管理
注解及接口:@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理;@Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用; 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
传播机制:
- OPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- OPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
- OPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
- OPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
- OPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,执行当前逻辑,结束后恢复上下文的事务。
- OPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。
默认传播机制:PROPAGATION_REQUIRED;
AOP
- Aspect切面:一个分布在应用程序中多个位置的标准代码/功能,通常与实际的业务逻辑(例如事务管理)不同。 每个切面都侧重于一个特定的横切功能。
- Joinpoint连接点:这是程序执行中的特定点,如方法执行,构调用造函数或字段赋值等。
- Advice通知:在一个连接点中,切面采取的行动。
- Pointcut切点:一个匹配连接点的正则表达式。 每当任何连接点匹配一个切入点时,就执行与该切入点相关联的指定通知。
- Weaving织入:链接切面和目标对象来创建一个通知对象的过程。
AOP之所以能够为动态生成的Bean提供代理,得益于PostProcessor接口。在IOC初始化流程中,最后一部,就是得到BeanFactory之中所有继承了PostProcessor接口的bean,调用它们的postProcessBeforeInitilization、postProcessAfterInitilization方法,来代理bean,生成新的bean。
Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
AspectJ织入方式
编译时织入:AspectJ编译器同时加载我们切面的源代码和我们的应用程序,并生成一个织入后的类文件作为输出。 编译后织入:这就是所熟悉的二进制织入。它被用来编织现有的类文件和JAR文件与我们的切面。 加载时织入:这和之前的二进制编织完全一样,所不同的是织入会被延后,直到类加载器将类加载到JVM。
Spring AOP织入方式
运行时织入,在使用目标对象的代理执行应用程序时,编译这些切面(使用JDK动态代理或者CGLIB代理)。
Spring AOP
是一个基于代理的AOP框架。这意味着,要实现目标对象的切面,将会创建目标对象的代理类。这可以通过下面两种方式实现:
JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。 CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。
不能跨越“final”的类来应用横切关注点(或切面),因为它们不能被覆盖,从而导致运行时异常。
同样地,也不能应用于静态和final的方法。由于不能覆写,Spring的切面不能应用于他们。因此,Spring AOP由于这些限制,只支持执行方法的连接点。 切面不适用于同一个类中调用的方法。这很显然,当我们在同一个类中调用一个方法时,我们并没有调用Spring AOP提供的代理的方法。
springboot原理
- @SpringBootApplication:@Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合
- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
- @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
- @Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类,
- 在@Configuration中被@Bean标记的方法,会被Spring进行CGLIB代理,从而进行增强。 在获取到所有的bean defenition之后,Spring会有一些post process执行,其中一个就是ConfigurationClassPostProcessor, 在这里,Spring会遍历所有的bean definition, 如果发现其中有标记了@Configuration注解的,会对这个类进行CGLIB代码,生成一个代理的类,并且把这个类设置到BeanDefenition的Class属性中。当需要拿到这个bean的实例的时候,会从这个class属性中拿到的Class对象进行反射,那么最终反射出来的是代理增强后的类。
- Configuration注解是被初始化的流程:
AbstractApplicationContext::refresh-->AbstractApplicationContext::invokeBeanFactoryPostProcessors -->ConfigurationClassPostProcessor::postProcessBeanFactory-->ConfigurationClassPostProcessor::enhanceConfigurationClasses
- 对于@Bean标记的方法,返回的都是一个bean,在增强的方法中,Spring会先去容器中查看一下是否有这个bean的实例了,如果有了的话,就返回已有对象,没有的话就创建一个,然后放到容器中。
- @RestController注解是@Controller和@ResponseBody的合集
Spring5
- 1,对jdk8增强,jdk9兼容;
- 2,函数式编程支持;
- 3,HTTP/2;
- 4,反应式编程模型;