spring框架 (半成品软件)学习笔记

本笔记视频地址

高度抽取可重用代码的一种设计;高度的通用性。 书城:WebUtils.java;BaseServlet;Filter... 打包:bootstore.jar;(工具类) commons-fileupload,commons-io(工具类),commons-dbutils(方便操作数据库的工具)

框架

抽取成一种高度课重用的;事务控制,强大的servlet,项目的一些工具。 框架:多个可重用模块的集合,形成一个某个领域的整体解决方案;

Spring(基于spring4.0)

容器(可以管理所有的组件(类))框架 核心关注:IOC和AOP 三个一组:source是源码包

Spring模块划分图:

  1. Test:Spring的单元测试模块; jar包:

    • spring-test-4.0.0.RELEASE.jar
  2. Core_Contaniner:核心容器(IOC);黑色代表这部分的功能由哪些jar包组成;要使用这个部分的完整功能,这些jar都需要导入 jar(ioc)包:

    • spring-beans-4.0.0.RELEASE.jar
    • spring-context-4.0.0.RELEASE.jar
    • spring-core-4.0.0.RELEASE.jar
    • spring-expression-4.0.0.RELEASE.jar
  3. AOP+Aspects(面向切面编程模块)

    jar包:

    • spring-aop-4.0.0.RELEASE.jar
    • spring-aspects-4.0.0.RELEASE.jar
  4. Data Access(数据访问):Spirng数据库访问模块

    • spring-jdbc-4.0.0.RELEASE.jar
    • spring-orm(Object Relation Mapping)-4.0.0.RELEASE.jar
    • spring-oxm(Object Xml Mapping)-4.0.0.RELEASE.jar
    • spring-jms-4.0.0.RELEASE.jar(消息服务--先不管)
    • spring-tx-4.0.0.RELEASE.jar(事务) oxm与jms为Integration
  5. Web:Spring开发web应用的模块

    jar包:

    • spring-web-4.0.0.RELEASE.jar:和原生的web相关(servlet)
    • spring-websocket(最近流行)-4.0.0.RELEASE.jar
    • spring-webmvc-4.0.0.RELEASE.jar:开发web项目的(web)
    • spring-webmvc-portlet-4.0.0.RELEASE.jar:开发web应用的组件集成 用哪个模块导哪个包(建议)

spring官网地址 文档地址(本地):D:\software\java学习资料\ssm\Spring\Spring\lib\spring-framework-4.0.0.RELEASE\docs\spring-framework-reference\htmlsingle\index.html

开发Spring框架的应用,经常要写框架的配置文件,写起来复杂,我们需要提示; 需要给eclipse 中安装插件;(提供提示功能) 插件安装(如果用idea,不需要安装)


特性

  1. 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
  2. 依赖注入:DI——Dependency Injection,反转控制(IOC)最经典的实现。
  3. 面向切面编程:Aspect Oriented Programming——AOP
  4. 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
  5. 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  6. 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。

IOC和AOP

1、IOC(Inversion(反转)Of Control):控制反转;

容器:管理所有的组件(有功能的类);假设,BookServlet受容器管理,BookService也受容器管理;容器可以自动的探查出那些组件(类)需要用到另一些组件(类);容器帮我们创建BookService对象,并把BookService对象赋值过去; 容器:主动的new资源变为被动的接受资源; 控制:资源的获取方式;

  • 主动式:(要什么资源都自己创建即可)
    BookServlet{
        BookService bs = new BookService();
        AirPlane ap = new AirPlane();//复杂对象的创建是比较庞大的工程;
    }   
  • 被动式:资源的获取不是我们自己创建,而是交给一个容器来创建和设置;
    BookServlet{
        BookService bs;
        public void test01(){
            bs.checkout();
        }
    }

2、DI(Dependency Injection)依赖注入;(IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入)

容器能知道哪个组件(类)运行的时候,需要另外一个类(组件);容器通过反射的形式,将容器中准备好的BookService对象注入(利用反射给属性赋值)到BookServlet中; 只要容器管理的组件,都能使用容器提供的强大功能; ①IOC在Spring中的实现

  1. 在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
  2. Spring提供了IOC容器的两种实现方式
    • BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
      • ApplicationContext: BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
  3. 在初始化时就创建单例的bean,也可以通过配置的方式(scope)指定创建的Bean是多实例(容器启动不创建,获取时创建)的。

以后框架编写流程:

  • 导包 4个核心包(beans--context-core-expression),若是eclipse,需要导入commons-logging-1.1.3.jar日志包
  • 写配置 spring的配置文件中,集合了spring的ioc容器管理的所有组件; 创建一个Spring Bean Configuration File(Spring的bean配置文件) - 测试 ApplicationContext的主要实现类:
    • new ClassPathXmlApplicationContext("ioc.xml");--->ioc容器的配置文件在类路径下
    • FileSystemXmlApplicationContext("D://ioc.xml");--->ioc容器的配置文件在磁盘路径下

②获取bean的方式

  • 通过id值 实例化IOC容器后,通过getBean("id值")便可以获取
  • 通过bean的类型 使用该类实例化,如Person person = ioc.getBean(Person.class);若有多个bean为该类型(Person),则报错,只能单实例

③给bean的属性赋值

  • 通过bean的setXxx()方法赋值 若想赋值null:
    <bean id="person01" class="com.zhm.bean.Person">
        <property name="lastName">
            <null/>
        </property>
    </bean>
  • 通过bean的构造器赋值 推荐:
    • 通过索引值指定参数位置
    • 通过类型不同区分重载的构造器
    • 给bean的级联属性赋值
          <!-- 在Person中有car的属性,这里用外部引入(ref) -->
          <bean id="car01" class="com.zhm.bean.Car">
              <property name="carName" value="宝马">
              <property name="color" value="绿色">
              <property name="price" value="1000000">
          </bean>
          <bean id="person01" class="com.zhm.bean.Person">
              <property name="car" ref="car01"></property>
          </bean>
          <!-- 也可以如下,引用内部bean(Car=new Car()) -->
          <bean id="person01" class="com.zhm.bean.Person">
              <property name="car">
                  <bean id="car01" class="com.zhm.bean.Car">
                      <property name="carName" value="奔驰"/>
                      <property name="color" value="黑色"/>
                      <property name="price" value="1000000"/>
                  </bean>
              </property>
          </bean>
          <!-- 复杂属性赋值 -->
          <bean id="book01" class="com.zhm.bean.Book">
              <property name="bookName" value="红楼梦">
          </bean>
          <bean id="person01" class="com.zhm.bean.Person">
              <!-- list赋值 -->
              <property name="books">
                  <list>
                      <!-- 内部bean无法通过id获取,即写id与不写都一样book000x -->
                      <bean id="book000x" class="com.zhm.bean.Book" p:bookName="西游记"></bean>
                      <!-- 引用外部的bean -->
                      <ref bean="book01"/>
                  </list>
              </property>
              <!-- map赋值 -->
              <property name="maps">
                  <map>
                      <entry key="key01" value="xxx"/>
                      <entry key="key02" value="19"/>
                      <!-- 引入外部值 -->
                      <entry key="key03" value-ref="book01"/>
                  </map>
              </property>
          </bean>
          <!-- Property类型 -->
          <property name="properties">
              <!-- 所有的k=v都是string -->
              <props>
                  <prop key="username">root</prop>  
                  <prop key="password">123456</prop>  
              </props>
          </property>
          <!-- person中有个属性是car,若要修改属性的属性,如下图: -->
      
  • 特别(非重要) 使用p命名空间(名称空间是为了防止标签重复),简化XML的配置方式 如:
    <bean id="studentSuper" class="com.zhm.ssm.bean.Student"
    p:studentId="2002" p:stuName="Jerry2016" p:age="18" />```
    
  • util名称空间创建集合类型的bean,方便别引用
  • 静态工厂 1、创建一个工厂类,里面有一个方法 2、bean中配置,factory-method指定方法,若不指定,ioc容器启动会创建实例就不是静态工厂 3、用constructor-arg传参 说明:静态工厂方法:对象 = 静态工厂类名.工厂方法名()
  • 实例工厂(对象 = new 工厂类名) 1、先配置出实例工厂对象 2、配置我们要创建的AirPlane使用哪个工厂创建
    • factory-bean:指定使用哪个工厂实例
    • factory-method:使用哪个工厂方法
  • Spring内部接口FactoryBean 1、实现该接口,ioc容器启动的时候不会创建实例(单,多实例都一样) 2、FactoryBean:获取时才创建对象 bean的写法与普通bean一样
  • spring连接池 配置bean的类型="com.mchange.v2.c3p0.ComboPoolDataSource" 引入外部属性文件需要依赖context名称空间,标签:
    <context:property-placeholder location="classpath:类路径的资源"/>
    <!-- 取出值 -->
    ${key}:动态取出配置文件中某个key的值
    注意:username是spring中key的一个关键字
    
  • 通过注解方式添加bean Spring有四个注解:
    • @Controller:控制器;我们推荐给控制器层(servlet包下的这些)的组件加这个注解
    • @Service:业务逻辑;我们推荐业务逻辑层的组件添加这个注解;BookService
    • @Repository:给数据库层(持久化层,dao层)的组件添加这个注解
    • @Component:给不属于以上几层的组件添加这个注解 写完注解后需要在bean文件中扫描:
    <!-- 需要依赖于aop包,才能够组件扫描 -->
    <context:component-scan base-package="com.zhm"/>
    <!-- 组件的id为类的首字母小写且单实例
        也可以在注解后面直接指定bean的id,如:@Repository("指定的id")
        当然也能够用注解@Scope(value="prototype"),指定为多实例
    -->
    
    • DI,属性的自动注入,用@Autowired

3、AOP

AOP:(Aspect Oriented Programming)面向切面编程 OOP:(Object Oriented Programming)面向对象编程 面向切面编程:基于OOP基础之上新的编程思想; 指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式,面向切面编程;

AOP使用场景:
    ① AOP加日志保存到数据库;
    ② AOP做权限验证;
    ③ AOP做安全检查
    ④ AOP做事务控制

jdk反射包下基于接口的动态代理(原生):

public class CalculatorProxy{
    public static Calculator getProxy(final Calculator calculator){
        //方法执行器
        InvocationHandler h = new InvocationHandler(){
            /**
            * Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
            * Method method:当前将要执行的目标对象的方法
            * Object[] args:这个方法调用时外界传入的参数值
            */
            @override
            public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
                
                 Object result = null;
                //利用反射执行方法
                //result:目标方法执行后的返回值
                try{
                    //执行方法之前
                    System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+Arrays.asList(args)+"]");
                    //方法执行
                    result = method.invoke(calculator,args);
                    //方法执行后
                    System.out.println("["+method.getName()+"]方法执行完成,计算结果是:"+result);
                }catch(Exception e){
                    //e.printStackTrace();
                    System.out.println("["+method.getName()+"]方法执行出现异常,异常信息是:"+e.getCause());
                }finally{
                    System.out.println("["+method.getName()+"]方法最终结束");
                }
                
                return result;
            }
        };
        Class<?>[] interfaces = calculator.getClass().getInterfaces();

        ClassLoader loader = calculator.getClass().getClassLoader();
        //创建代理对象
        Object proxy = Proxy.newProxyInstance(loader,interfaces,h);
        return (Calculator)proxy;
    }   
}
AOP的专业术语:

3.1、编写步骤(注解方式):

1、导包:

  • 基础包 spring-aspects-4.0.0.RELEASE.jar
  • 加强版(非官方) com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

2、写配置

  • 目标类和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中
    • 在类上加注解
    • 在配置文件中进行包扫描:<context:component-scan base-package="com.zhm"/>
  • 告诉Spring到底哪个是切面类(在类上加注解@Aspect)
  • 告诉Spring,切面类的方法何时运行,在通知方法上加注解
    • @Before:在目标方法之前运行
    • @After:在目标方法执行结束之后
    • @AfterReturning:在目标方法正常返回之后
    • @AfterThrowing:再目标方法抛出异常之后运行
    • @Around:环绕,见下图
  • 在注解上加入切入点表达式,格式:execution(访问权限符 返回值类型 方法全类名(参数列表))
    • @Before("execution(public int com.zhm.impl.MyMathCalculator.*(int,int))")
    • 匹配一个或多个字符:execution(public int com.zhm.impl.MyMath*r.*(int,int))
    • 匹配任意一个参数(这里的第一个参数为int,第二个任意类型):execution(public int com.zhm.impl.MyMathCalculator.*(int,*))
    • 匹配任意多个参数,任意类型参数(两个点表示):execution(public int com.zhm.impl.MyMathCalculator.*(..))
    • 匹配任意多层路径:execution(public int com.zhm..MyMathCalculator.*(int,int))
  • 开启基于aop注解的AOP功能
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

3、测试

  • 获取目标方法的详细信息
    • 只需要在通知方法的参数列表上写一个参数:JoinPoint joinPoint--》封装了当前目标方法的详细信息 并通过joinPoint.getXXX()获取参数等等信息(getArgs()、getSignature()-->getName())

    • 接受返回值 切入点表达式是value="切入点表达式",returning="result":表示result接受返回值(在方法上加入该参数后在注解上告诉Spring该参数是什么),异常信息(同上):throwing="e"

3.2、基于配置的AOP

1、导包同上 2、将目标类和切面类加入到ioc容器中

    <bean id="目标类的id" class="xxxx.类名"></bean>
    <bean id="切面类的id" class="xxx.类名"></bean>

3、AOP配置

<aop:config>
    <!-- 指定切面:同@Aspect -->
    <aop:aspect ref="切面类的id">
        <!-- 抽取切入点表达式 -->
        <aop:pointcut id="mypointcut" expression="execution(public int xxx.*.*(..))">
        <!-- 配置哪个方法是前置通知:method指定方法名  pointcut:指定切入点表达式 -->
        <aop:before method="方法名" pointcut="execution(public int xxx.*.*(..))"/>
        <aop:after-returning method="方法名" pointcut-ref="mypointcut" returning="指定的返回值"/>
        <aop:after-throwing method="方法名" pointcut-ref="mypointcut" throwing="exception"/>
        <aop:after method="方法名" pointcut-ref="mypointcut"/>
    </aop:aspect>
</aop:config>

4、声明式事务

事务:

操作数据库; Spirng提供了JdbcTemplate能快捷的操作数据库 JdbcTemplate和QueryRunner;

  1. 导入操作数据库的包(使用JdbcTemplate操作数据库) orm、tx、jdbc、(oxm,jms不用导入)
  2. 连接数据源
    <!-- ${}:取出配置文件中的值 -->
    <!-- #{}:spring的表达式语言(spel??) -->
    <context:property-placeholder location="classpath:dbconfig.properties">
    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="driverClass" value="${jdbc.driverClass}"/>
    </bean>
    <!-- 使用JdbcTemplate操作数据库需要获取数据源 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>
    可以直接获取该bean(jdbcTemplate)操作数据库了

注意: 查询单个集合:jdbcTemplate.query() 查询单个对象:jdbcTemplate.queryForObject()

  1. 声明式事务 以前通过复杂的编程来编写一个事务,替换为只需要告诉Spring哪个方法是事务方法即可; Spring自动进行事务控制;

    AOP:环绕通知可以去做; //获取连接 //设置非自动 提交 目标代码执行 //正常提交 //异常回滚 //最终关闭

    注解配置:

        <!-- 事务控制 -->
        <!-- 1. 导包:aop基础包与增强包 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 控制数据源 -->
            <property name="dataSource" ref="dataSource"/>
        </bean>
        <!-- 2. 开启基于注解的事务控制模式 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>
        <!-- 3. 给事务方法加注解@Transactional -->
        <!-- 注解后面的细节 -->
        异常分类:
            运行时异常(非检查异常):可以不用处理,默认回滚
            编译时异常(检查异常):要么try-catch,要么在方法上声明throws,默认不回滚
        ① timeout=3:超时,秒为单位,事务超出指定执行时长后自动终止并回滚
        ② readOnly=true:加快查询速度(只读事务);不用管事务那一堆操作。
        ③ noRollbackFor={ArithmeticException.class,NullPointerException.class}:(运行异常)默认回滚的可以设置为不回滚(编译时异常)
        ④ noRollbackForClassName={"com.xxx.类名"},效果与上面一个一样
        ⑤ rollbackFor={类名.class}:原本不回滚(编译时异常)的异常指定让其回滚     
    

    @Transactional(isolation=Isolation.READ_UNCOMMITTED) 隔离级别: 一个事务与其他事务隔离的程度称为隔离级别。为什么会有这种现象??

    假设现在有两个事务:Transaction01和Transaction02并发执行。

    ①脏读 1、Transaction01将某条记录的AGE值从20修改为30。 2、Transaction02读取了Transaction01更新后的值:30。 3、Transaction01回滚,AGE值恢复到了20。 4、Transaction02读取到的30就是一个无效的值。 ②不可重复读 1、Transaction01读取了AGE值为20。 2、Transaction02将AGE值修改为30。 3、Transaction01再次读取AGE值为30,和第一次读取不一致。 ③幻读 1、Transaction01读取了STUDENT表中的一部分数据。 2、Transaction02向STUDENT表中插入了新的行。 3、Transaction01读取了STUDENT表时,多出了一些行。 如何解决这些问题呢??? ①读未提交:READ UNCOMMITTED---->造成脏读

    允许Transaction01读取Transaction02未提交的修改。

    ②读已提交:READ COMMITTED

    要求Transaction01只能读取Transaction02已提交的修改。

    ③可重复读:REPEATABLE READ-->mysql下能解决不可重复读与幻读的问题

    确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。

    ④串行化:SERIALIZABLE

    确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

    ⑤各个隔离级别解决并发问题的能力见下表

    脏读 不可重复读 幻读
    READ UNCOMMITTED
    READ COMMITTED
    REPEATABLE READ
    SERIALIZABLE

    ⑥各种数据库产品对事务隔离级别的支持程度

    Oracle MySQL
    READ UNCOMMITTED ×
    READ COMMITTED
    REPEATABLE READ × √ (默认)
    SERIALIZABLE

    事务的传播行为:在@Transactional(propagation=Propagation.REQUIRED) xml配置:

  2. 编程式事务:

    TransactionFilter{ try{ //获取连接 //设置非自动提交 //chain.doFilter(); //提交 }catch(Exception e){ //回滚 }finally{ //关闭连接释放资源 } }

  3. Spring与JavaWeb的整合使用 1)Spring来控制事务(dao--JdbcTemplate) 2) 所有的组件Autowired 3) 管理数据库


本文章使用limfx的vscode插件快速发布