美文网首页
spring 核心知识

spring 核心知识

作者: jiahzhon | 来源:发表于2020-04-13 17:53 被阅读0次

    什么是spring

    • spring是一个轻量级的IOC和AOP容器框架。是为JAVA应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。

    主要模块

    Spring AOP : AOP服务
    Spring Core : 核心库类,提供IOC服务
    Spring Context : 提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等)
    Spring DAO : 对JDBC的抽象 ,简化了数据库访问异常的处理
    Spring ORM : 对现有的ORM框架的支持
    Spring Web : 提供了基本的面向Web的综合特性
    Spring MVC : 提供面向Web应用的Model-View-Controller实现

    spring IOC

    • Inversion of Control 控制反转
    • 所谓 IOC ,就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系。
    • Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,然后根据这张注册表实例化 Bean,装配好 Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中 Bean 缓存池为 HashMap 实现。

    BeanFactory

    • 是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
    • 定义了允许在运行期间向容器注册单实例 Bean 的方法;对于单实例( singleton)的 Bean 来说,BeanFactory 会缓存 Bean 实例,所以第二次使用 getBean() 获取 Bean 时将直接从IoC 容器的缓存中获取 Bean 实例。
    • 在初始化 BeanFactory 时,必须为其提供一种日志框架,比如使用 Log4J, 即在类路径下提供 Log4J 配置文件,这样启动 Spring 容器才不会报错。

    ApplicationContext

    • 应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;

      • ClassPathXmlApplicationContext:默认从类路径加载配置文件

      • FileSystemXmlApplicationContext:默认从文件系统中装载配置文件

      • 国际化(MessageSource):为应用提供 i18n 国际化消息访问的功能;

      • 访问资源,如URL和文件(ResourceLoader)

      • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

      • 消息发送、响应机制(ApplicationEventPublisher)让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。

      • AOP(拦截器)

    • ClassPathXmlApplicationContext

      • 简单的用 ApplicationContext 做测试的话 , 获得 Spring 中定义的 Bean 实例(对象) 可以用:
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      • 如果是两个以上 , 可以使用字符串数组 :
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml","SpringTest.xml"});
      • 或者可以使用通配符:
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/*.xml");
        对于 ClassPathXmlApplicationContext 的使用:
    • classpath: 前缀是可加可不加的 , 默认就是指项目的 classpath 路径下面。
      如果要使用绝对路径 , 需要加上 file: , 前缀表示这是绝对路径。

    spingboot源码指的classpath

    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { 
        "classpath:/META-INF/resources/", 
        "classpath:/resources/", 
        "classpath:/static/", 
        "classpath:/public/" }; 
    
    • 对于 FileSystemXmlApplicationContext 的使用:

      • 没有盘符的是项目工作路径 , 即项目的根目录。
      • 有盘符表示的是文件绝对路径 ,file: 可加可不加。
      • 如果要使用 classpath 路径 , 需要前缀 classpath:。
    • ApplicationEventPublisher

    • ApplicationContext 通过 ApplicationEvent 类和 ApplicationListener 接口进行事件处理。 如果将实现 ApplicationListener 接口的 bean 注入到上下文中,则每次使用 ApplicationContext 发布 ApplicationEvent 时,都会通知该 bean。本质上,这是标准的观察者设计模式。

    • 事件定义

    public class UserRegisterEvent extends ApplicationEvent{
        public UserRegisterEvent(String name) { //name即source
            super(name);
        }
    }
    
    • 事件发布者
    @Service
    public class UserService implements ApplicationEventPublisherAware {
        private ApplicationEventPublisher applicationEventPublisher;
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
        public void register(String name) {
            System.out.println("用户:" + name + " 已注册!");
            applicationEventPublisher.publishEvent(new UserRegisterEvent(name));
        }
    }
    
    • 事件订阅者
    @Service
    public class EmailService implements ApplicationListener<UserRegisterEvent> {
        @Override
        public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
            System.out.println("邮件服务接到通知,给 " + userRegisterEvent.getSource() + " 发送邮件...");
        }
    }
    
    • WebApplication 体系架构
      • WebApplicationContext 是专门为 Web 应用准备的,它允许从相对于 Web 根目录的路径中装载配置文件完成初始化工作。从 WebApplicationContext 中可以获得ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到 ServletContext 中,以便 Web 应用环境可以访问 Spring 应用上下文。


        image.png

    BeanFactory和ApplicationContext

    两者装载bean的区别:
    BeanFactory:
    BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;

    ApplicationContext:
    ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;

    各自优点:

    • 延迟实例化的优点:(BeanFactory)
      应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势;

    • 不延迟实例化的优点: (ApplicationContext)

    1. 所有的Bean在启动的时候都加载,系统运行的速度快;

    2. 在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题

    3. 建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成)

    Spring Bean 作用域(用于判断bean的个数)

    • singleton(单例)、prototype(原型)、request、session 和 global session


      image.png
    • singleton:单例模式(多线程下不安全,此为spring默认的生命周期)

      • singleton:单例模式,Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:
        <bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>
    • prototype:原型模式(每次使用时创建)

      • 每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton作用域。
    • Request:一次 request 一个实例

      • request:在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean实例也将会被销毁。
        <bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
    • session

      • 在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。
    • global Session

      • global Session:在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效。

    Spring Bean 生命周期

    1. Spring对Bean进行实例化(相当于程序中的new Xx())

    2. Spring将值和Bean的引用注入进Bean对应的属性中,利用IOC

    3. 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)

    4. 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)

    5. 如果Bean实现了ApplicationContextAware接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )

    6. 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,(增加某个功能,这个很重要,AOP就是在这里实现的)

    7. 如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。

    8. 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 ) 这个时候已经再被使用了

    9. 经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁

    10. 如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

    • 主要逻辑都在doCreate()方法中,逻辑很清晰,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应,非常重要,在后续扩展接口分析中也会涉及。
      • createBeanInstance() -> 实例化
      • populateBean() -> 属性赋值
      • initializeBean() -> 初始化
    // 忽略了无关代码
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
          throws BeanCreationException {
    
       // Instantiate the bean.
       BeanWrapper instanceWrapper = null;
       if (instanceWrapper == null) {
           // 实例化阶段!
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
    
       // Initialize the bean instance.
       Object exposedObject = bean;
       try {
           // 属性赋值阶段!
          populateBean(beanName, mbd, instanceWrapper);
           // 初始化阶段!
          exposedObject = initializeBean(beanName, exposedObject, mbd);
       }
    
       
       }
    

    当容器关闭时调用bean的销毁方法

    Spring Bean 依赖注入

    ● 理解基于构造函数的依赖注入

    /*带参数,方便利用构造器进行注入*/ 
     public CatDaoImpl(String message){ 
     this. message = message; 
     } 
    <bean id="CatDaoImpl" class="com.CatDaoImpl"> 
    <constructor-arg value=" message "></constructor-arg> 
    </bean>
    

    ● 理解基于设置函数的依赖注入

    public class Id { 
     private int id; 
     public int getId() { return id; } 
     public void setId(int id) { this.id = id; } 
    } 
    <bean id="id" class="com.id "> <property name="id" value="123"></property> </bean>
    

    ● 基于自动装配的依赖注入(配置文件里不用写依赖关系了,有相应的方法就行)
    byType(类型模式),byName(名称模式)、constructor(构造函数模式)

    ● 基于注解的依赖注入(方法都省略了)
    @Autowired @Resource

    Spring AOP

    AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
    1、切面(aspect):一般就是一个类。切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
    2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。 (相对于核心关注点来说的)
    3、连接点(joinpoint):连接点是在应用执行过程中能够插入切面(Aspect)的一个点。这些点可以是调用方法时、甚至修改一个字段时。虚的概念
    4、切入点(pointcut):在哪些类,哪些方法上切入(where)
    5、通知(advice):在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能),通知分为前置、后置、异常、最终、环绕通知五类。
    6、目标对象:代理的目标对象。
    7、织入(weave):把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
    8、引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。

    • 连接点和切入点的理解
      • 连接点:连接点是一个虚拟的概念,可以理解为所有满足切点扫描条件的所有的时机。

      • 具体举个例子:比如开车经过一条高速公路,这条高速公路上有很多个出口(连接点),但是我们不会每个出口都会出去,只会选择我们需要的那个出口(切点)开出去。

      • 简单可以理解为,每个出口都是连接点,但是我们使用的那个出口才是切点。每个应用有多个位置适合织入通知,这些位置都是连接点。但是只有我们选择的那个具体的位置才是切点。

    @Aspect
    public class TransactionDemo {
       @Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
       public void point(){
       }
    
       @Before(value="point()")
       public void before(){
         System.out.println("transaction begin");
       }
    
       @AfterReturning(value = "point()")
       public void after(){
         System.out.println("transaction commit");
       }
    
       @Around("point()")
       public void around(ProceedingJoinPoint joinPoint) throws Throwable{
         System.out.println("transaction begin");
         joinPoint.proceed();
         System.out.println("transaction commit");
     } }
    

    相关文章

      网友评论

          本文标题:spring 核心知识

          本文链接:https://www.haomeiwen.com/subject/lhpnmhtx.html