https://spring.io/

一、Spring 组成

image.png

二、Spring Bean 作用域

默认情况下Sping只为每个在IOC容器里面声明的bean创建唯一一个实例,整个IOC容器范围内只能共享该实例,该作用域被称为singleton

  • singleton 在SpingIOC容器中仅存在一个bean实例,Bean以单实例的方式存在。
  • prototype 每次调用getBean()时都会返回一个新的实例。
  • request 每次Http请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境。
  • session 同一个HTTP Session共享一个bean,不同的HTTP Session适用不同的Bean。该作用域仅适用于WebApplicationContext环境。

    三、Spring Bean 是否线程安全

    Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。 线程安全 https://www.yuque.com/wangchao-volk4/fdw9ek/qky2rb#oQA9L

1、分析

原型Bean
对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean
对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

2、解决bean的线程安全问题

1、加注解@Scope(value = “prototype”)

  1. public class MyConfig {
  2. @Bean
  3. @Scope(value = "prototype")
  4. public User user(){
  5. return new User();
  6. }
  7. }

2、对成员变量使用ThreadLocal

3、总结

1、在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
2、尽量不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。
3、默认注入的Bean对象,在不设置scope的时候他也是线程不安全的。
4、一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的。

四、IOC

控制反转,把创建对象的过程和对象之间的调用过程,交给Spring进行管理。 目的:耦合度降低。 IOC的简单实现如下:

  1. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  2. // 获取配置创建的对象
  3. User user = context.getBean("user", User.class);
  4. System.out.println(user);
  5. user.add();

1、IOC原理

5、Spring IOC 容器和对象的创建流程

2、IOC接口(BeanFactory)

1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
2、Spring提供IOC容器实现方式:(两个接口)
(1)BeanFactory:IOC容器基本实现,是Sping内部的使用接口,不提供开发人员使用。加载配置文件的时候不会去创建对象,在获取(使用)对象的时候才去创建。
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多强大的功能,一般由开发人员使用。在加载配置文件的时候就会创建对象。
image.png
3、ApplicationContext接口有实现类就是我们常用的一些创建bean的类
image.png

3、Bean 管理

Spring有两种bean,一种是普通bean,另外一种是工厂bean (FactoryBean)

3.1 普通bean

如下里面的context.getBean,就是返回一个普通bean

  1. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  2. // 获取配置创建的对象
  3. User user = context.getBean("user", User.class);
  4. System.out.println(user);
  5. user.add();

3.2 工厂bean (FactoryBean)

在配置文件定义bean类型可以和返回类型不一样,代码如下可见,Dog对象的获取是靠getObject,这里明显返回的是User。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="user" class="com.supking.service.impl.UserImpl"></bean>
  7. <bean id="dog" class="com.supking.factorybean.Dog"></bean>
  8. </beans>
  1. public class Dog implements FactoryBean<User> {
  2. @Override
  3. public User getObject() throws Exception {
  4. return new UserImpl();
  5. }
  6. @Override
  7. public Class<?> getObjectType() {
  8. return null;
  9. }
  10. }

执行如下代码则报错:

  1. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  2. Dog dog = context.getBean("dog", Dog.class);
  3. System.out.println(dog);
  4. //报错信息如下,期望的是Dog,实际是UserImpl
  5. Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'dog' is expected to be of type 'com.supking.factorybean.Dog' but was actually of type 'com.supking.service.impl.UserImpl'

需要修改返回值,代码如下,指定返回值为User,则不会报错:

  1. public static void main(String[] args) {
  2. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  3. // 注意配置文件中dog对应的类是com.supking.factorybean.Dog
  4. // <bean id="dog" class="com.supking.factorybean.Dog"></bean>
  5. User user = context.getBean("dog", User.class);
  6. System.out.println(user);
  7. }
  8. 输出:
  9. com.supking.service.impl.UserImpl@6500df86

3.3 装配方式

3.3.1 手动装配

  1. <bean id="orders" class="com.supking.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
  2. <property name="name" value="phone"></property>
  3. </bean>
  4. <bean id="emp" class="com.supking.autowire.Emp">
  5. <property name="dept" ref="dept"></property>
  6. </bean>
  7. <bean id="dept" class="com.supking.autowire.Dept"></bean>

3.3.2 自动装配

  1. <!-- 实现自动装配
  2. autowire,配置自动装配
  3. autowire属性常用两个值 byName:根据名字 type:根据类型(缺点,多个相同类型的bean会失败)
  4. -->
  5. <bean id="emp" class="com.supking.autowire.Emp" autowire="byName"></bean>
  6. <bean id="dept" class="com.supking.autowire.Dept"></bean>

4、注解管理 bean

注解是什么?
11-注解 注解是代码标记,格式,@注解名称(属性名称=属性值,………),可以作用在类、方法、属性上面; 使用注解的目的:简化xml的配置

4.1 依赖和配置

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aop</artifactId>
  4. <version>${spring.version}</version>
  5. </dependency>
  1. <!--告知spring在创建容器时,要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束中
  2. use-default-filters:false,不再设置默认filter(扫描所有东西),知己配置filter
  3. -->
  4. <context:component-scan base-package="com.supkingx" use-default-filters="false">
  5. <!-- 指定扫描哪些内容-->
  6. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  7. </context:component-scan>
  8. <context:component-scan base-package="com.supkingx">
  9. <!-- 指定不扫描哪些内容-->
  10. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  11. </context:component-scan>

4.2 创建Bean的注解

@Component、@Service、@Controller、@Repository,这四个注解功能一样,都是用来创建 bean 实例的。

4.3 基于注解实现注入

@Autowired:根据属性类型进行自动装配
@Qualifier(value=”userDaoImpl“):根据属性名称进行注入,需要和@Autowired一起使用
@Resource(name=”userDaoImpl“):可以根据类型注入,可以根据名称注入
@Value:注入普通类型

4.4 完全注解开发

无 xml

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.supkingx"})
  3. public class SpringConfig {
  4. }
  1. // 使用AnnotationConfigApplicationContext
  2. public static void main(String[] args) {
  3. ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
  4. UserService userService = context.getBean(UserService.class);
  5. userService.add();
  6. }

五、AOP

Apsect Oriented Programming面向切面:不修改源代码进行功能的增强。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合性降低,提高程序的可用性,同时提高了开发效率。 通俗描述:不通过修改源代码,在主干方法里面添加新功能。

1、优先级

  • 前置通知 (@Before) 。
  • 返回通知 (@AfterReturning) 。
  • 异常通知 (@AfterThrowing) 。
  • 后置通知 (@After)。
  • 环绕通知 (@Around) :(优先级最高)

    1. @Aspect
    2. @Component
    3. public class SysTimeAspect {
    4. /**
    5. * 切入点
    6. */
    7. @Pointcut("bean(sysMenuServiceImpl)")
    8. public void doTime(){}
    9. @Before("doTime()")
    10. public void doBefore(JoinPoint jp){
    11. System.out.println("time doBefore()");
    12. }
    13. @After("doTime()")
    14. public void doAfter(){//类似于finally{}代码块
    15. System.out.println("time doAfter()");
    16. }
    17. /**核心业务正常结束时执行
    18. * 说明:假如有after,先执行after,再执行returning*/
    19. @AfterReturning("doTime()")
    20. public void doAfterReturning(){
    21. System.out.println("time doAfterReturning");
    22. }
    23. /**核心业务出现异常时执行
    24. * 说明:假如有after,先执行after,再执行Throwing*/
    25. @AfterThrowing("doTime()")
    26. public void doAfterThrowing(){
    27. System.out.println("time doAfterThrowing");
    28. }
    29. @Around("doTime()")
    30. public Object doAround(ProceedingJoinPoint jp)
    31. throws Throwable{
    32. System.out.println("doAround.before");
    33. try {
    34. Object obj=jp.proceed();
    35. return obj;
    36. }catch(Throwable e) {
    37. System.out.println("doAround.error-->"+e.getMessage());
    38. throw e;
    39. }finally {
    40. System.out.println("doAround.after");
    41. }
    42. }
    43. }

    image.png
    image.png

    2、springboot1和2 对 AOP的影响

    boot1对应spring4,boot2对应spring5

image.png
spring4和spring5的AOP顺序对比结果
image.png
综上图中可以看到,spring5之后,@After的优先级被放到(@afterReturn和@AfterThrowing)之后了,@Around的环绕后通知被置到最后

3、AOP 术语

  • 连接点:类里面那些方法可以被增强,这些方法称为连接点
  • 切入点:实际上真正被增强的方法
  • 通知(增强):实际增加的那一部分逻辑,即在原方法的前面和后面增加的那一部分逻辑
    • 前置、后置、环绕、异常、最终
  • 切面:是一个过程,即把通知应用到切入点的过程。

表达式:权限修饰符 返回来下 全类名路径 方法名称 (参数列表)
列如

  1. @After(value = "execution(* com.supkingx.UserService.add(..))")
  1. // 提取出表达式
  2. @Pointcut(value = "execution(* com.supkingx.UserService.add(..))")
  3. public void pointdemo(){
  4. }
  5. @After(value="pointdemo()")
  6. public void xxx(){
  7. .............
  8. }

六、循环依赖

7、Spring 循环依赖

七、Bean 的生命周期

3、Spring 生命周期