• Spring


    源码辅助阅读:https://github.com/tripleDemo/spring.github.io

    spring 技术概述

    分层 一站式 java ee full-stack 轻量级框架

    三层:

    1:数据访问层(持久层):主要是对数据库的操作,而不是数据,具体为业务逻辑层或表示层提供数据服务。

    2:业务逻辑层:主要是针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理。

    3:界面层:主要表示WEB方式,也可以表示成WINFORM方式,WEB方式也可以表现成:aspx,如果逻辑层相当强大和完善,无论表现层如何定义和更改,逻辑层都能完善地提供服务。

    struts2 web:表现层的MVC框架

    Hibernate:是数据访问层(持久化)的完整的orm框架

    full-stackspring 框架,包括javaee三层的每一层解决方案)

    web:springmvc(servlet),springboot

    业务层:bean管理AOP(面向切面,系统级功能,抽取)

    持久化:jdbctemplate,ORM

    特点:

    1.方便解耦,简化开发

    通过Spring提供的IoC容器,将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

    2.AOP编程的支持

    通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。

    3.声明事物的支持

    Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。

    4.方便程序的测试

    可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

    5.方便集成各种优秀框架

    Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。

    6.降低Java EE API的使用难度

    Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。

    体系结构

    IoC和DI思想

    IoCInversionof Control(控制反转):读作“反转控制”更好理解,是一种设计思想。就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。

    正控:若调用者需要使用某个对象,其自身就得负责该对象及该对象所依赖对象的创建和组装。

    返控:调用者只管负责从Spring容器中获取需要使用的对象,不关心对象的创建过程,也不关心该对象依赖对象的创建以及依赖关系的组装,也就是把创建对象的控制权反转给了Spring框架。

    DIDependency Injection(依赖注入)从字面上分析:IoC指将对象的创建权,反转给了Spring容器;DISpring创建对象的过程中,将对象依赖属性(常量,对象,集合)通过配置设置给该对象。

    IoC从字面意思上很难体现出谁来维护对象之间的关系,Martin Fowler提出一个新的概念DI,更明确描述了“被注入对象(Service对象)依赖IoC容器来配置依赖对象(Dao对象)”。

    Spring IoC 容器(Container)

    BeanFactory : Spring最底层的接口,只提供了的IoC功能,负责创建、组装、管理bean,在应用中,一般不使用BeanFactory,而推荐使用ApplicationContext(应用上下文)。

    ApplicationContext接口继承了BeanFactory,除此之外还提供AOP集成、国际化处理、事件传播、统一资源加载等功能。

    public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
    MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

    bean的创建时机(此时不使用Spring Test

    1. BeanFactory需要等到获取某一个bean的时候才会创建bean,延迟初始化
    2. ApplicationContext在启动Spring容器的时候就会创建所有的beanWeb应用建议)

    bean 实例化方式

    1. 构造器实例化(无参数构造器),最标准,使用最多
    2. 静态工厂方法实例化:解决系统遗留问题
    3. 实例工厂方法实例化:解决系统遗留问题
    4. 实现FactoryBean接口实例化:实例工厂变种,如集成MyBatis框架使用:org.mybatis.spring.SqlSessionFactoryBean(掌握)

    bean的作用域 : Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围。<bean id=”” class=”” scope=”作用域”/>(在开发中主要使用scope=”singleton”scope=”prototype”)

    singleton : 单例,在Spring IoC容器中仅存在一个Bean实例(默认的scope)。

    prototype : 多例,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean():不会再容器启动时创建对象。

    request : 用于web开发,将Bean放入request范围,request.setAttribute(“xxx”),在同一个request获得同一个 Bean

    session : 用于web开发,将Bean放入Session范围,在同一个Session获取同一个Bean

    globalSession : 一般用于Porlet应用环境,分布式系统存在全局session概念(单点登录),如果不是porlet环境,globalSession等同于Session

    application : Scopes a single bean definition to the lifecycle of a ServletContext.Only valid in the context of a web-aware Spring ApplicationContext.

    websocket : Scopes a single bean definition to the lifecycle of a WebSocket.Only valid in the context of a web-aware Spring ApplicationContext.

    总结:对于Struts1中的Action使用requestStruts2中的Action使用prototype类型,其他使用singleton

     

    DI

    DI从字面上分析:IoC指将对象的创建权,反转给了Spring容器。

    DISpring创建对象的过程中,将对象依赖属性(常量、对象、集合)通过配置设值给该对象。

    通过XML设置装配

    1. XML自动装配(不推荐)
    2. setter注入(开发用的较多)

    1,使用bean元素的<property>子元素设置

    常量类型,直接使用value赋值;

    引用类型,使用ref赋值;

    集合类型,直接使用对应的集合类型元素即可。

    2,spring通过属性的setter方法注入值

    3,属性的设置值是在init方法执行之前完成的

    1. 构造器注入
    2. bean元素继承

    多个bean元素共同配置的抽取,实则是bean配置的拷贝,和Java的继承不同。

    Java的继承:把多个类共同的代码抽取到父类中。

    bean元素的继承(inheritance):把多个bean元素共同的属性配置抽取到另一个公用的bean元素中。

    1. property place holder

     

    通过注自动装配

    DI注解

    Autowired

    1.可以让Spring自动的把属性需要的对象找出来,并注入到对象

    2.可以贴在字段或者setter方法上面

    3.可以同时注入多个对象

    @Autowired

    public void setter(OtherBean otherBean,OtherBean other2){}

    4.可以注入一些Spring内置的重要对象,比如BeanFactory,ApplicationContext,ServletContext等;

    5.默认情况下Autowired注解必须要能找到对应的对象,否则报错。通过required=false来避免这个问题:@Autowired(required=false)

    6.第三方程序:Spring3.0之前,需要手动配置Autowired注解的解析程序:<context:annotation-config/>(在Web开发中必须配置)

    7.Autowired注解寻找bean的方式:

    1)首先按照依赖对象的类型找,如果找到,就使用setter或者字段直接注入;

    2)如果在Spring上下文中找到多个匹配的类型,再按照名字去找,如果没有匹配报错;

    3)可以通过使用@Qulifier(“other”)标签来规定依赖对象按照beanid和类型的组合方式去找;

    Resource

    1.可以让Spring自动的把属性需要的对象找出来,并注入到对象

    2.可以贴在字段或者setter方法上面

    3.可以注入一些Spring内置的重要对象,比如BeanFactory,ApplicationContext,ServletContext等;

    4.Resource注解必须要能找到对应的对象,否则报错。

    5.第三方程序:Spring3.0之前,需要手动配置Resource注解的解析程序:<context:annotation-config/>(在Web开发中必须配置)

    6.Resource注解寻找bean的方式:

    1)首先按照名字去找,如果找到,就使用setter或者字段注入;

    2)如果按照名字找不到,再按照类型去找,但如果找到多个匹配类型,报错;

    3)可以直接使用name属性指定bean的名称@Resource(name=“”),但是如果指定的name就只能按照name去找,如果找不到就不会再按照类型去找;

    Value

    ...

     

    IoC注解

    bean组件版型:四个组件的功能是相同的,只是用于标注不同类型的组件。

    @Component泛指组件,当组件不好归类的时候,可以使用这个注解进行标注。

    @Repository用于标注数据访问组件,即DAO组件。

    @Service用于标注业务层组件。

    @Controller用于标注控制层组件(如struts中的ActionSpringMVCController)。

    此时需要配置IoC注解的解析器:

    使用<context:component-scan base-package=""/>

    表示去哪些包中及其子包中去扫描组件注解

     

    作用域注解

    @Scope简单点说就是用来指定bean的作用域

     

    初始和销毁注解

    @PostConstruct//构建的对象之后

    public void open() {

      System.out.println("初始化方法");

    }

    @PreDestroy//销毁之前

    public void close() {

      System.out.println("销毁前扫尾方法");

    }

     

    AOP

    代理模式:

    客户端直接使用的是代理对象,不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介的作用。

    1. 代理对象完全包含真实对象,客户端使用的都是代理对象的方法,和真实对象没有直接关系;
    2. 代理模式的职责:把不是真实对象该做的事情从真实对象上掀开--职责清晰;

     

    静态代理:

    在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在运行前就确定了。

    优点:

    1. 业务类只需要关注业务逻辑本身,保证了业务类的重用性。
    2. 把真实对象隐藏起来了,保护真实对象。

    缺点:(解决:动态代理)

    1. 代理对象的某个接口只服务与某一种类型的对象,也就是说每一个真实对象都得创建一个代理对象。
    2. 如果需要代理的方法很多,则要为每一种方法都进行代理处理。
    3. 如果接口增加一个方法,除了所有实现类需要实现这个方法外,代理类也需要实现此方法。

     

    动态代理:

    动态代理类是在程序运行期间由JVM通过反射等机制动态生成的,所以不存在代理类的字节码文件,代理对象和真实对象的关系是在程序运行时期才确定的。

    如何实现动态代理:

    1)针对有接口:使用JDK动态代理

    2)针对无接口:使用CGLIBJavassist组件

    JDK动态代理

    JDK动态代理API分析:(必须要求真实对象是有接口)

    1、java.lang.reflect.Proxy类:Java动态代理机制生成的所有动态代理类的父亲,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

    主要方法:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)

    方法职责:为指定类加载器、一组接口及调用处理器生成动态代理类实例

    参数:loader:类加载器,一般传递真实对象的类加载器

      interfaces:代理类需要实现的接口

      handler:代理对象如何做增强

    返回:创建的代理对象

    //创建一个代理对象

    public <T> T getProxyObject(){

    return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),//类加载器,一般跟上真实对象的类加载器

    target.getClass().getInterfaces(),//真实对象所实现的接口(JDK动态代理必须要求真实对象有接口)

    this);//如何做事务增强的对象

    }

    2、java.lang.reflect.InvocationHandler接口:public Object invoke(Object proxy,Method method,Object[] args)

    方法职责:负责集中处理动态代理类上的所有方法调用

    参数:proxy:生成的代理对象

      method:当前调用的真实方法对象

      args:当前调用方法的实参

    返回:真实方法的返回结果

    操作步骤:

    1. 实现InvocationHandler接口,创建自己增强代码的处理器。
    2. Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。
    3. 在处理器中实现增强操作

    CGLIB动态代理

    1、 //创建一个代理对象

    public <T> T getProxyObject(){

    Enhancer enhancer = new Enhancer();

    enhancer.setSuperclass(target.getClass());//将继承于哪一个类,去做增强

    enhancer.setCallback(this);//设置增强的对象

    return (T) enhancer.create();//创建代理对象

    }

    2org.springframework.cglib.proxy.InvocationHandler接口:public Object invoke(Object proxy,Method method,Object[] args)

    拦截器思想

    百度百科:java里的拦截器是动态拦截Action调用的对象。在AOPAspect-Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。

    拦截器与Filter过滤器很相似,Fileter就是对请求和响应做拦截。

    FilterWeb领域的概念,只能针对于请求和响应做增强,离不开servlet-api.jar

    Interceptor:整个Java领域的概念,不仅可以运用到servlet层,还可以用到Web

     

    代理总结:

    JDK动态代理总结:

    1.JAVA动态代理是使用java.lang.reflect包中Proxy类与InvocationHandler接口这两个来完成的。

    2.要使用JDK动态代理,委托必须要定义接口。

    3.JDK动态代理将会拦截所有public的方法(因为只能调用接口中定义的方法),这样即使接口中添加了新的方法,不用修改代码也会被拦截。

    4.动态代理的最小单位是类(所有类中的方法都会被处理),如果只想拦截一部分方法,可以在invoke方法中对要执行的方法名进行判断。

    CGLIB代理总结:

    1. CGLIB可以生产委托类的子类,并重写父类非final修饰符的方法。
    2. 要求类不能是final的,要拦截的方法要是非final、非static、非private的。
    3. 动态代理的最小单位是类(所有类中的方法都会被处理)。

    关于性能:

    JDK动态代理是基于实现接口的,CGLIBJavassit是基于继承委托类的。

    从性能上考虑:Javassit > CGLIB > JDK

    Struts2的拦截器和Hibernate延迟加载对象,采用的是Javassit的方式。

    对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规范。

    若委托类对象实现了干接口,优先选用JDK动态代理。

     

    AOP思想

    把一个个的横切关注点放到某个模块中去,称之为切面。那么每一个的切面都能影响业务的某一种功能,切面的目的就是功能增强,如日志切面就是一个横切关注点,应用中许多方法需要做日志记录的只需要插入日志的切面即可。这种面向切面编程的思想就是AOP思想。

    行业术语

    Joinpoint:连接点,被拦截到需要被增强的方法。where:去哪里做增强

    Pointcut:切入点,哪些包中的哪些类中的哪些方法,可认为是连接点的集合。where:去哪些地方做增强

    Advice:增强,当拦截到Joinpoint之后,在方法执行的什么时机(when)做什么样(what)的增强。根据时机分为:前置增强、后置增强、异常增强、最终增强、环绕增强

    public void doWork() {

      //前置增强

      try {

        //业务代码

        //后置增强

      } catch(Exception e) {

        //异常处理

        //异常增强

      } finally {

        //释放资源

        //最终增强

      }

    }

    Aspect:切面,Pointcut+Advice,去哪些地方+在什么时候+做什么增强

    Target:目标对象,被代理的目标对象

    Weaving:织入,把Advice加到Target上之后,创建出Proxy对象的过程

    Proxy:一个类被AOP织入增强后,产生的代理类

    Pointcut语法

    1. AspectJ是一个面向切面的框架:

    AspectJ切入点语法如下(表示在哪些包下的哪些类的哪些方法上做切入增强):

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

    翻译成中文:execution(<修饰符>? <返回类型> <声明类型>? <方法名>(<参数>) <异常>?)

    举例:public static Class java.lang.Class.forName(String className)throws ClassNotFoundException

    1. 通配符:

    *:匹配任何部分,只能表示一个单词

    ..:可用于全限定名中和方法参数中,分别表示子包和ON个参数

     

    Spring持久层支持

     

     

    Spring事务

    何为数据库事务

    事务是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务(Transaction/tx)

    事务必需满足ACID(原子性、一致性、隔离性和持久性)特性,缺一不可:

    原子性(Atomicity):事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做。

    一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据依然处于正确的状态,即数据完整性约束没有被破坏,如AB转账,不论转账成功与否,转账之后的AB的账户总额和转账之前是相同的。

    隔离性(Isolation):当多个事务处于并发访问同一个数据库资源时,事务之间相互影响程度,不同的隔离级别决定了各个事务对数据资源访问的不同行为。

    持久性(Durability):事务一旦执行成功,它对数据库的数据的改变是不可逆的。

     

    数据库并发问题

    并发问题类型

    产生原因和效果

    第一类丢失更新

    两个事务更新相同数据,如果一个事务提交,另一个事务回滚,第一个事务的更新会被回滚

    脏读

    第二个事务查询到第一个事务未提交的更新数据,第二个事务根据该数据执行,但第一个事务回滚,第二个事务操作脏数据

    虚读(幻读)

    一个事务查询到了另一个事务已经提交的新数据,导致多次查询数据不一样

    不可重复读

    一个事务查询到另一事务已经修改的数据,导致多次查询数据不一致

    第二类丢失更新

    多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变

    事务的隔离级别

    为了解决这些并发问题,需要通过数据库隔离级别来解决,在标准SQL规范中定义了四种隔离级别:

    READ UNCOMMITED < READ COMMITED < REPEATABLE READ < SERIALIZABLE

    隔离级别

    脏读

    不可重复读

    幻读

    第一类丢失更新

    第二类丢失更新

    READ UNCOMMITED

    ×

    READ COMMITED

    ×

    REPEATABLE READ

    ×

    ×

    SERIALIZABLE

    ×

    ×

    ×

    ×

    ×

    默认情况下:MySQL不会出现幻读,除非:select * from 表名 lock in share mode;

    MySQL中锁基于索引机制,也不会出现第一类丢失更新。

    Oracle支持READ COMMITED(缺省)SERIALIZABLE

    MySQL支持四种隔离级别,缺省为REPEATABLE READ

    如何选用:

    隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少,因此在实际项目开发中为了考虑并发性能一般使用READ COMMITED。它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,更多的情况下使用悲观锁或乐观锁来解决。

    (悲观锁:FOR UPDATE。乐观锁:定义一个列,来表示每次修改的版本。)

    事务的类型

    本地事务和分布式事务:

    本地事务:就是普通事务,能保证单台数据库上的操作的ACID,被限定在一台数据库上;

    分布式事务:涉及多个数据库源的事务,及跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,使事务可以跨越多台数据库;

    JDBC事务和JTA事务:

    JDBC事务:就是数据库事务类型中的本地事务,通过Connection对象的控制来管理事务;

    JTA事务:JTA(Java Transaction API),是Java EE数据库事务规范,JTA只提供了事务管理接口,由应用程序服务器厂商提供实现,JTA事务比JDBC更强大,支持分布式事务。

    按是否通过编程实现事务有声明式事务和编程式事务:

    编程式事务:通过编写代码来管理事务。

    声明式事务:通过注解或XML配置来管理事务。

    Spring事务管理

    Spring的事务管理主要包括3个接口:

    PlatformTransactionManager:平台的事务管理器,是多种事务管理器的基类,涵盖了处理事务的方法。根据TransactionDefinition提供的事务属性配置信息,创建事务。

     

    TransactionDefinition:封装事务的隔离级别和超时时间,是否为只读事务和事务的隔离级别和传播规则等事务属性。

    TransactionStatus:封装了事务的具体运行状态。如是否是新开启事务,是否已经提交事务,设置当前事务为rollback-only等。

    事务传播规则

    事务是如何在这些方法间传播的,Spring共支持7种传播行为:

     

    情况一:需要遵从当前事务

    REQUIRED:必须存在有一个事务,如果当前存在一个事务,则加入到该事务中,否则,新建一个事务,使用比较多的情况。

    SUPPORTS:支持当前事务,如果当前存在事务,则使用该事务,否则,以非事务形式运行。

    MANDATORY:必须要存在事务,如果当存在事务,就使用该事务,否则,非法的事务状态异常(IllegalTranactionStatusException)

    情况二:不遵从当前事务的

    REQUIRES_NEW:不管当前是否存在事务,都会新开启一个事务,必须是一个新的事务,使用的也比较多。

    NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,把当前事务挂起(暂停)

    NEVER:不支持事务,如果当前存在事务,抛出一个异常。

    情况三:寄生事务(外部事务/内部事务/嵌套事务)

    NESTED:寄生事务,如果当前存在事务,则在内部事务内执行;如果当前不存在事务,则创建一个新的事务。寄生事务可以通过数据库savePoint(保存点)来实现,寄生事务可以回滚的,但是他的回滚不影响外部事务,但是外部事务的回滚会影响寄生事务。

    寄生事务并不是所有的事务管理器都支持,比如HibernateTransactionManager默认就不支持,需要手动去开启。JdbcMyBatis的事务管理器:DataSourceTransactionManager:默认就是支持的。

    事务配置

    <!-- 配置一个CRUD的通用事务的配置 -->

    <tx:advice id="crudAdvice" transaction-manager="txManager">

      <tx:attributes>

        <!-- service中的查询方法 -->

        <tx:method name="get*" read-only="true" propagation="REQUIRED"/>

        <tx:method name="list*" read-only="true" propagation="REQUIRED"/>

        <tx:method name="query*" read-only="true" propagation="REQUIRED"/>

        <!-- service中其他方法(非查询) -->

        <tx:method name="*" propagation="REQUIRED"/>

      </tx:attributes>

    </tx:advice>

     

     

  • 相关阅读:
    bae问题
    union intersect minus
    在搭建SpringMvc Hibernate框架时遇到的问题
    详细解读Spring2.5 +Struts1.3 框架(使用Spring声明式事物管理和springjjdbc模板)
    javaee缓存技术 oscache ehcache
    Warning
    Math
    Test
    网络流24题-最小路径覆盖问题
    Wannafly summer camp Day3--Knight
  • 原文地址:https://www.cnblogs.com/tripleDemo/p/10798838.html
Copyright © 2020-2023  润新知