Spring基础

59 阅读11分钟

Spring基础

什么是Spring

  • Spring是一个提供了更完善开发环境的一个框架,可以为POJO(Plain Ordinary Java Object)对象提供企业级的服务
  • 通过IOC容器创建并管理对象的生命周期
  • 可以通过使用XML和Java注解组合各种对象,协作完成各种复杂任务
  • 可以便捷的整合各种第三方库与开源框架

IoC容器

简述

  • IoC(Inversion of Control)控制反转,不是什么技术,而是一种设计思想。
  • 通过将对象的创建权交给 Spring 去维护与管理,从对象间的依赖转变为依赖IoC容器。
  • 它能指导我们如何设计出松耦合、更优良的程序
  • 控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。
  • 通俗来说就是IoC是设计思想,DI是实现方式
  • DI(Dependency Injection)
    • 依赖注入,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值

对象的三种装配方式

  • xml 配置

    <bean id="demoService" class="cn.demo.demoService">
    
  • Java 配置

    @Bean 
    public DemoService demoService() { 
    	return new DemoService()
    }
    
  • 注解配置

    @Component 
    public class TestService{
    	...
    }
    

对象依赖注入的方式

XML配置注入

  • 构造函数注入

    <bean id="demoService" class="cn.demo.DemoService">
    	<!-- 通过有参构造函数注入 -->
    	<constructor-arg name="demoDao" value="demoDao" />
    </bean>
    <bean id="demoDao" class="cn.demo.DemoDao"></bean>
    
  • 属性注入

    <bean id="demoService" class="cn.demo.DemoService">
    	<!--通过setName方法注入-->
    	<property name="demoDao" value="DemoDao"/>
    </bean>
    

注解注入

  • 构造函数注入

    @Component
    class TestService{
        TestNextService testNextService;
    
        public TestService(@Autowired TestNextService testNextService) {
        }
    }
    
  • 属性注入

    @Component
    class TestService{
        String name;
    
        @Value("你好")
        public void setName(String name) {
            this.name = name;
        }
    }
    

常用注解

  • @Autowired
    • 是Spring自带的注解,是根据类型(byType )进行自动装配的,如果有多个类型一样的Bean候选者,需要指定按照名称(byName )进行装配,则需要配合
  • @Qualifier
  • @Resource
    • 是JSR250规范的实现,在javax.annotation包下,默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name进行指定进行注入
  • @Inject
    • 是JSR330 (Dependency Injection for Java)中的规范
    • 需要导入javax.inject.Inject jar包 ,才能实现注入
    • @Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合
  • @Named

IoC容器相关类的说明

BeanFactory

  • 简述

    • 工厂模式定义了IOC容器的基本功能规范
  • ListableBeanFactory

    • 该接口定义了访问容器中 Bean 基本信息的若干方法
    • 如查看Bean 的个数、获取某一类型 Bean 的配置名、查看容器中是否包括某一 Bean 等方法
  • HierarchicalBeanFactory

    • 父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器
    • 通过该接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。
    • 比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。
    • 这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean
  • ConfigurableBeanFactory

    • 增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法
  • ConfigurableListableBeanFactory

    • ListableBeanFactory 和 ConfigurableBeanFactory的组合
  • AutowireCapableBeanFactory

    • 将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法

BeanRegistry

  • 向IOC容器手工注册 BeanDefinition 对象的方法

BeanDefinition

  • 定义了各种Bean对象及其相互的关系

  • 相关类

    • BeanDefinitionReader。BeanDefinition的解析器
    • BeanDefinitionHolder。BeanDefination的包装类,用来存储BeanDefinition,name以及aliases等

ApplicationContext

  • 是IoC容器的接口类,提供并管理应用的上下文

  • 通过实现了ResourcePatternResolver,提供资源的访问

  • 通过实现MessageSource接口,提供国际化处理

  • 通过实现ApplicationEventPublisher接口,提供应用事件处理

  • 从上下文中获取对象示例

    @Component
    class TestService{
        @Autowired
        ApplicationContext context;
    
        public void say() {
            TestConfig config = context.getBean(TestConfig.class);
        }
    }
    
    @Configuration
    class TestConfig {
    
        @Value("文本")
        private String text;
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    }
    

AOP

简述

  • AOP(Aspect Oriented Programming)面向切面编程,通过声明切入点与连接点,并指定织入方式,以实现切面编程
  • Spring Aop使用了动态代理技术实现的切面编程。目标对象是接口的场景使用的是JDK动态代理,目标对象是普通类则使用的是CGLib动态代理

AOP应用分析

  • 自定义定义一个AOP

    • @Aspect。用来定义一个切面
    • @Pointcut。用于定义切入点表达式
  • 前置通知(Before advice)

    • 在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)
    • @Before。用于定义前置通知
  • 后置通知(After returning advice)

    • 在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回
    • @AfterReturning。用于定义后置通知
  • 异常通知(After throwing advice)

    • 在方法抛出异常退出时执行的通知
    • @After-Throwing。用于定义异常通知
  • 最终通知(After (finally) advice)

    • 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
    • @After。用于定义最终通知
  • 环绕通知(Around Advice)

    • 包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行
    • @Around。用于定义环绕通知
  • 示例

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    //用来定义一个切面
    @Aspect
    @Component
    class AopTest{
    
    //    用于定义切入点表达式
        @Pointcut("execution(* cn.demo.TestService.*(..))")
        public void pointCut(){}
    
    
    //    用于定义前置通知
        @Before("pointCut()")
        public void before() {
            System.out.println("before");
        }
    
    //    用于定义后置通知
        @AfterReturning("pointCut()")
        public void afterReturning() {
            System.out.println("afterReturning");
        }
    
    //    用于定义异常通知
        @AfterThrowing("pointCut()")
        public void afterThrowing() {
            System.out.println("afterThrowing");
        }
    
    //    用于定义最终通知
        @After("pointCut()")
        public void after() {
            System.out.println("after");
        }
    
    //    用于定义环绕通知
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint pjp) {
            System.out.println("around");
            Object result = "test";
            try {
                result = pjp.proceed();
            } catch (Throwable e) {
                System.out.println(e.getMessage());
            }
            return result;
        }
    }
    

相关术语

  • 连接点(Jointpoint)
  • 切入点(Pointcut)
  • 通知(Advice)
  • 方面/切面(Aspect)
  • 引入(inter-type declaration)
  • 目标对象(Target Object)
  • 织入(Weaving)
  • AOP代理(AOP Proxy)

Spring AOP和AspectJ是什么关系

  • AspectJ是更强的AOP框架,是实际意义的AOP标准
  • Spring AOP采用的就是基于运行时增强的代理技术,在运行时动态将要增强的代码织入到目标类中
  • ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入

OOP

OOP(Object Oriented Programming)面向对象编程

MVC

简述

  • MVC英文是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计规范,本质上也是一种解耦
  • 是一种基于Java 的实现了Web MVC 设计模式的请求驱动类型的轻量级Web 框架,即使用了MVC 架 构模式的思想,将 web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开 发,Spring Web MVC 也是要简化我们日常Web 开发的

相关组件

DispatcherServlet

  • 前端控制器,也称为中央控制器,
  • 它是整个请求响应的控制中心,组件的调用由它统一调度

HandlerMapper

  • 处理器映射器,它根据用户访问的 URL 映射到对应的后端处理器 Handler
  • 也就是说它知道处理用户请求的后端处理器
  • 但是它并不执行后端处理器,而是将处理器告诉给中央处理器

HandlerAdapter

  • 处理器适配器,它调用后端处理器中的方法,返回逻辑视图 ModelAndView 对象

ViewResolver

  • 视图解析器,将 ModelAndView 逻辑视图解析为具体的视图(如 JSP)

Handler

  • 后端处理器,对用户具体请求进行处理,也就是我们编写的 Controller 类

HandlerExecutionChain

  • Handler
  • HandlerInterceptor

ModleAndView

  • 逻辑视图

Filter(ServletFilter)

  • 进入Servlet前可以有preFilter, Servlet处理之后还可有postFilter

LocaleResolver

  • 在视图解析/渲染时,还需要考虑国际化(Local),显然这里需要有LocaleResolver

ThemeResolver

  • ThemeSource接口和ThemeResolver,包含一些静态资源的集合(样式及图片等),用来控制应用的视觉风格

MultipartResolver

  • 文件的上传请求

流程分析

  1. User Request
    • 用户发起请求
  2. DispatcherServlet
    1. 将用户请求交给HandlerMapper进行处理
    2. 并返回HandlerExecutionChain
  3. DispatcherServlet
    1. 通过Handler创建HandlerAdapter
    2. 然后HandlerAdapter中调用invokeHandlerMethod进行处理(内部进行了Controller层调用处理)
    3. 并返回ModelAndView
  4. DispatcherServlet
    1. 将ModleAndView交给ViewResolver进行解析,
    2. 返回View。
    3. 然后将Modle传给View进行视图渲染
  5. 响应
    • 将控制器给DispatcherServlet,并由DispatcherServlet将响应返回给用户

Spring Bean管理

Bean初始化的多级缓存

  • 第一层缓存(singletonObjects)
    • 单例对象缓存池,已经实例化并且属性赋值,这里的对象是成熟对象
  • 第二层缓存(earlySingletonObjects)
    • 单例对象缓存池,已经实例化但尚未属性赋值,这里的对象是半成品对象
  • 第三层缓存(singletonFactories)
    • 单例工厂的缓存

Bean生命周期分析

Bean自身的方法

  • Bean本身调用的方法
  • 通过配置文件中< bean >的init-method和destroy-method指定的方法

Bean级生命周期接口方法

  • BeanNameAware
  • BeanFactoryAware
  • ApplicationContextAware
  • InitializingBean
    • @PostConstruct
  • DisposableBean
    • @PreDestroy

容器级生命周期接口方法

  • 一般称它们的实现类为“后处理器”

  • InstantiationAwareBeanPostProcessor

  • BeanPostProcessor

工厂后处理器接口方法

  • 工厂后处理器接口的方法,工厂后处理器也是容器级的,在应用上下文装配配置文件之后立即调用

  • AspectJWeavingEnabler

  • ConfigurationClassPostProcessor

  • CustomAutowireConfigurer

Bean循环依赖

单例

  • 通过三级缓存解决单例的循环依赖

多例

  • 多实例Bean是每次调用一次
  • getBean都会执行一次构造方法并且给属性赋值,根本没有三级缓存
  • 因此不能解决循环依赖

prototype(原型)

  • spring不会缓存‘prototype’作用域的bean
  • 因此无法自动解决

循环依赖常见处理方式

  • 使用@Lazy注解,延迟加载
  • 使用@DependsOn注解,指定加载先后关系
  • 修改文件名称,改变循环依赖类的加载顺序
  • 多例对象依赖改为单例
  • 构造器注入对象

动态代理技术

简述

  • 动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术
  • 代理模式(Proxy pattern),为另一个对象提供一个替身或占位符以控制对这个对象的访问
  • 在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作

实现方式

jdk代理

  • JDK动态代理是有JDK提供的工具类Proxy实现的

  • 动态代理类是在运行时生成指定接口的代理类

  • 每个代理实例(实现需要代理的接口)都有一个关联的调用处理程序对象

  • 此对象实现了InvocationHandler

  • 最终的业务逻辑是在InvocationHandler实现类的invoke方法上

  • 示例

    @Component
    public class JdkProxyTest {
    
        @Bean
        public UserService userService() {
            UserServiceImpl service = new UserServiceImpl();
            JdkProxy proxy = new JdkProxy(service);
            return (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), proxy);
        }
    }
    
    class JdkProxy implements InvocationHandler {
        private Object target;
        public JdkProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("proxy before");
            Object result = method.invoke(target, args);
            System.out.println("proxy after");
            return result;
        }
    }
    
    interface UserService{
        String queryInfo();
    }
    
    class UserServiceImpl implements UserService {
        @Override
        public String queryInfo() {
            String info = "test jdk proxy";
            System.out.println(info);
            return info;
        }
    }
    

cglib代理

  • 简述

    • Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截
    • cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)。ASM是操作字节码的工具
    • SpringAOP基于cglib进行封装,实现cglib方式的动态代理
  • 依赖

    <dependency>  
    	<groupId>cglib</groupId>  
    	<artifactId>cglib</artifactId>
    </dependency>
    
  • 示例

    @Component
    public class CglibProxyTest {
    
        @Bean
        public UserAddressService userAddressService() {
            CglibProxy proxy = new CglibProxy(new UserAddressService());
    
            //1. 创建一个工具类
            Enhancer enhancer = new Enhancer();
            //2. 设置父类
            enhancer.setSuperclass(UserAddressService.class);
            //3. 设置回调函数
            enhancer.setCallback(proxy);
            //4. 创建子类对象,即代理对象
            return (UserAddressService) enhancer.create();
        }
    
    }
    
    class UserAddressService{
    
        public String queryAddress() {
            String info = "test cglib proxy method";
            System.out.println(info);
            return info;
        }
    }
    class CglibProxy implements MethodInterceptor {
    
        private Object target;
    
        public CglibProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("cglib proxy before");
            Object result = methodProxy.invoke(target, args);
            System.out.println("cglib proxy after");
            return result;
        }
    }