• 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>

     

     

  • 相关阅读:
    数据类型装换
    变量及数据类型
    27 网络通信协议 udp tcp
    26 socket简单操作
    26 socket简单操作
    14 内置函数 递归 二分法查找
    15 装饰器 开闭原则 代参装饰器 多个装饰器同一函数应用
    12 生成器和生成器函数以及各种推导式
    13 内置函数 匿名函数 eval,exec,compile
    10 函数进阶 动态传参 作用域和名称空间 函数的嵌套 全局变量
  • 原文地址:https://www.cnblogs.com/tripleDemo/p/10798838.html
Copyright © 2020-2023  润新知