Spring基础知识

1. spring的注入方式

a . bean配置
  • 设值注入

    • setXxx()方法,
  • 构造注入

    • 标签
  • p命名空间

  • 自动装配 (只适用于引用类型)autowire=”byName | byType | constructor | no” (不建议多次使用)

    • 在< bean id=”” autowire=”byName” /> ,当id的类中有和IOC容器中bean的id相同时,自动装配
      byName : bean的id值=类的属性名
      byType : 其他bean的类型(class)是否与该类的ref属性类型一致(当前IOC容器中只能有一个bean满足条件)
      constructor : 其他bean的类型(class)是否与该类的构造方法参数的类型一致,本质是byType
    • 在头文件的beans标签中添加统一自动装配:default-autowire=”byName”
    • 自动装配虽然可以减少代码量,但是会降低程度的可读性,使用时需要谨慎
b . 注解方式
  • @Component细化:

    • @Repository Dao层
    • @Service service层
    • @Controller controller层
  • 配置包扫描器:

    xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context.xsd  
    <context:component-scan base-package="com.test.pojo" />  
  • 在声明前加上 @Value(“”)

2. 使用注解实现声明式事务

1.需要的包:

  • spring-tx.jar
  • ojdbc.jar 驱动
  • commons-dbcp.jar 连接池使用到的数据源
  • commons-pool.jar 连接池
  • spring-jdbc.jar
  • aopalliance.jar aop相关

2.增加事务tx的命名空间

3.增加对事务的支持(核心):

<tx:annotation-driven transaction-manager="txManager" />  

配置事务管理器:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
    <property name="dataSource" ref="dataSource"/>  
</bean>  

配置数据库相关:

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
    <property name="url" value="" />
    <property name="username" value="system" />
    <property name="password" value="123456" />
    <property name="maxIdle" value="10" />
    <property name="maxWaitMillis" value="1000" />
</bean>

4.将需要成为事务的方法 前增加注解:

@Transactional(readOnly=false,propagation=Propagation.REQUIRED,rollBackFor=...)

3. AOP

需要的jar:

  • aopliance.jar
  • aspectjweaver.jar

通知类型:org.springframework.aop.

  • 前置通知:MethodBeforeAdvice 接口方法 before()

  • 后置通知:AfterReturningAdvice 接口方法 afterReturning()

  • 异常通知:ThrowsAdvice 接口方法 无

  • 环绕通知:MethodInterceptor 接口方法 invoke()

    <!-- 配置前置通知  切面 -->  
    <bean id="logBefore" class="com.test.aop.LogBefore">  
    </bean>  
    <!-- 关联切入点和切面 -->
    <aop:config >
        <!-- 配置切入点 -->
        <aop:pointcut id="pointcut" expression="execution(public void com.test.service.demoService.DemoServiceImpl.addUser())" />
        <!-- 连接线 -->
        <aop:advisor advice-ref="logBefore" pointcut-ref="pointcut" />
    </aop:config>

异常通知必须实现方法:

public void afterThrowing(Throwable ex)
or
 public void afterThrowing(Method method, Object[] args, Object target, Throwable ex)

环绕通知:

 @Override
public Object invoke(MethodInvocation mi) throws Throwable {
    Object result = null;
    try {
        System.out.println("环绕-前置通知");

        //在此之前的为前置通知
        result = mi.proceed();      //控制目标方法是否执行

        //在此之后的为后置通知
        System.out.println("环绕-后置通知:目标对象:" + mi.getThis() + ",调用的方法:" + mi.getMethod().getName() +
                ",参数个数:" + mi.getArguments().length + ",返回值:" + result);
    }catch (Exception e){
        //异常通知
        System.out.println("环绕-异常通知");
    }
    return result;
}

注解实现AOP

  • 前置: @Before

  • 后置: @AfterReturning

  • 异常: @AfterThrowing

  • 环绕: @Around

  • 最终: @After

    注解形式AOP,需设置扫描器,扫描器会将指定的包中的 @Component @Service @Repository @Controller修饰的类产生的对象增加到IOC容器

    通过注解形式实现的AOP,若要获取目标对象的一些参数,则需要使用一个对象:JoinPoint

    @Component(“logAnnotation”)
    @Aspect //表示此类是一个AOP通知
    public class LogAnnotation {

    // 前置通知
    @Before("execution(public void addUser())")           // 属性定义触发时机(切点)
    public void myBefore(JoinPoint jp){
        System.out.println("《注解》-前置通知:目标对象:" + jp.getTarget() + ",方法名:" + jp.getSignature().getName() +
        ",参数个数:" +jp.getArgs().length );
    }
    
    // 后置通知
    @AfterReturning(pointcut = "execution(public void deleteUser())", returning = "returningValue")    // 属性定义触发时机(切点)
    public void myAfter(JoinPoint jp, Object returningValue){   //需要声明该方法有返回值, returningValue 是返回值
        System.out.println("《注解》-后置通知:目标对象:" + jp.getTarget() + ",方法名:" + jp.getSignature().getName() +
                ",参数列表:" + Arrays.toString(jp.getArgs()) + ",返回值:" + returningValue );
    }
    
    // 异常通知:如果只捕获特定类型的异常,则可以通过第二个参数实现:e
    @AfterThrowing(pointcut = "execution(public void addUser())", throwing = "e")
    public void myException(JoinPoint jp, NullPointerException e){
        System.out.println("《注解》-异常通知: " + e.getMessage());
    }
    
    // 环绕通知
    @Around("execution(public void addUser())")
    public void myAround(ProceedingJoinPoint pjp){
        //前置通知
        System.out.println("《注解-环绕》-前置通知");
        try{
            pjp.proceed();          // 执行方法,抛出的是Throwable
    
            System.out.println("《注解-环绕》-后置通知");
        }catch (Throwable e){
            // 异常通知
            System.out.println("《注解-环绕》-异常通知");
        }finally {
            // 最终通知
            System.out.println("《注解-环绕》-最终通知");
        }
    }
    //单独的最终通知
    @After("execution(public void addUser())")
    public void myAfter(){
        System.out.println("《注解-环绕》-【单独的最终通知】");
    }

    }

开启注解对AOP的支持

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

通过Scheme配置AOP

xmlns:aop="http://www.springframework.org/schema/aop"

类似于实现接口的方式

  • 编写一个普通的类

    // Schema实现的AOP不需要实现接口,只需要在xml里配置
    public class LogSchema {
        // 前置通知
        public void before(JoinPoint jp){
            System.out.println("【Schema】前置通知");
        }
        //后置通知
        public void afterReturning(JoinPoint jp, Object returnValue){
            System.out.println("【Schema】后置通知:目标对象:" + jp.getThis() + ",方法名:" + jp.getSignature().getName() + ",参数个数:"
             + jp.getArgs().length + ", 返回值:" + returnValue);
        }
        // 环绕通知
        public Object around(ProceedingJoinPoint pjp){
            System.out.println("【Schema】环绕-前置通知");
    
            Object result = null;
            try {
                //在此之前的为前置通知
                result = pjp.proceed();      //控制目标方法是否执行
                //在此之后的为后置通知
    
                System.out.println("【Schema】环绕-后置通知:目标对象:" + pjp.getThis() + ",调用的方法:" + pjp.getSignature().getName() +
                        ",参数个数:" + pjp.getArgs().length + ",返回值:" + result);
            }catch (Throwable e){
                //异常通知
                System.out.println("【Schema】环绕-异常通知");
            }
            return result;
        }
    }
  • 将该类通过配置,转为一个通知

如果要获取目标对象的信息

  • 注解schema:JoinPoint

  • 接口:Method method, Object[] args, Object target

     <!-- 通过Schema实现AOP -->
    <bean id="logSchema" class="com.test.aop.LogSchema">
    </bean>
    <!-- 关联切入点和切面 -->
    <aop:config >
        <!-- 配置切入点 -->
        <aop:pointcut id="pcSchema" expression="execution(public void com.test.service.demoService.DemoServiceImpl.addUser())" />
        <!-- 连接线  Schema方式不需要这个
            <aop:advisor advice-ref="logSchema" pointcut-ref="pcSchema" />
        -->
        <aop:aspect ref="logSchema">
            <!-- pointcut-ref:连接线      returnValue: 返回值 -->
            <aop:before method="before" pointcut-ref="pcSchema"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pcSchema" returning="returnValue"/>
            <aop:around method="around" pointcut-ref="pcSchema"  />
        </aop:aspect>
    </aop:config>

Spring 开发Web项目

Java项目中从main开始启动,可以初始化IOC容器,在Web项目中从服务器启动时,需通过监听器初始化IOC容器

需要用到 spring-web.jar ,并在web.xml中进行配置

<!-- 配置spring-web.jar 提供的监听器,可以在服务器启动时初始化IOC容器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <!--
        1. 必须告诉监听器此容器的位置:context-param
        2. 默认约定的位置:WEB-INF/applicationContext.xml
    -->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param> 

配置时必须告诉监听器此容器的位置,或者用默认的名称放在默认约定的位置:
WEB-INF/applicationContext.xml

拆分Spring配置文件

Web项目拆分配置:

  • 三层结构
    • UI(html/css/jsp/servlet)
    • Service
    • Dao
    • 公共 数据库
  • 功能结构
    • 学生管理系统的功能结构:学生相关配置、班级相关配置…

Web项目合并配置:

1.在web.xml的context-param的classpath:中配置

<param-value>
    classpath:applicationContext.xml,
    classpath:applicationContext-1.xml,
    classpath:applicationContext-2.xml
</param-value>    

或

(推荐)
<param-value>
    classpath:applicationContext.xml,
    classpath:applicationContext-*.xml
</param-value>

2.在主配置文件applicationContext.xml中引入其他文件

<import resource="applicationContext-*.xml" />
...

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
     version="4.0">

<!-- 配置spring-web.jar 提供的监听器,可以在服务器启动时初始化IOC容器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <!--
        1. 必须告诉监听器此容器的位置:context-param
        2. 或者放在默认约定的位置:WEB-INF/applicationContext.xml
    -->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
</web-app>

springIOC容器与Servlet容器搭建通道,传递数据 (servlet与service)

在Servlet的init初始化方法中,获取SpringIOC容器中的bean对象

ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext("");
xxxService  = (xxxxService) context.getBean("");

SpringMVC

Mybatis

MyBatis 可以简化JDBC操作,实现数据持久化

  • ORM: Object Relational Mapping
  • ORM概念:MyBatis是ORM的一个实现 / Hibernate
  • ORM可以使得开发人员,像操作对象一样操作数据库表

开发Mybatis步骤:

配置Mybatis

  1. jar包: mybatis.jar ojdbc6.jar
  • config.xml : 配置数据库信息 和 需要加载的映射文件
  • 表 - 类
  • 映射文件xxxMapper.xml : 增删改查标签 < select>
  • 测试类:sqlSession.selectOne(“需要查询的SQL的namespace.id”,”SQL的参数值”);

config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-config.dtd">

    <!--
    default和id值保持一致,通过default找到id
      -->
    <configuration>
    <environments default="development">   <!-- 默认环境,即环境的id -->
        <environment id="development">
            <!-- 配置事务的提交方式:
                    1.用JDBC方式进行事务处理(commit  rollback  close)
                    2.MANAGER:将事务交由其他组件去托管(spring, jobss),默认会关闭连接
                         <property name="closeConnection" value="false" />  // 设置为不关闭
             -->
            <transactionManager type="JDBC"/>

        <!-- 连接方式 UNPOOLED: 传统的连接方式(每次访问数据库都需要打开和关闭等操作)
                       POOLED: 使用数据库连接池
                         JNDI: 从tomcat获取一个内置的数据库连接池
         -->
        <dataSource type="POOLED">
            <property name="driver" value="oracle.jdbc.OracleDriver"/>
            <property name="url" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
            <property name="username" value="system"/>
            <property name="password" value="123456"/>

        </dataSource>
    </environment>
     <mappers>
        <!-- 在配置文件中加载映射文件 -->
        <mapper resource="personMapper.xml"/>
    </mappers>
</configuration>

xxxMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    namespace       映射文件路径
    id              区分查询语句
    resultType      返回值
    parameterType   传入参数的类型,这里是id的类型int
-->
<mapper namespace="com.pojo.personMapper">
        <select id="queryPersonById" resultType="com.pojo.Person" parameterType="int">
          select * from person where id = #{id}
       </select>
</mapper>

Mybatis的CRUD:

// 加载Mybatis文件,访问数据库
Reader reader = Resources.getResourceAsReader("config.xml");
// 当config.xml里的default不是所需的时,可以在build里强行切换数据库环境
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
// SqlSessionFactory -- Connection
SqlSession sqlSession = sessionFactory.openSession();

//  通过配置文件personMapper.xml里的namespace和id获得sql语句
String statement = "com.pojo.personMapper.addPerson";
Person person = new Person(8, "cui8", 27);
//  insert 插入一个person
int count = sqlSession.insert(statement, person);
//需要 commit
sqlSession.commit();

System.out.println("增加" + count + "个成功");
sqlSession.close();

Mybatis 约定
输入参数parameterType 和 输出参数resultType,在形式上都只能有一个
如果输入参数是简单类型(8个基本+String),是可以使用任何占位符#{xxx}的;
如果是对象类型,则必须是对象的属性:#{属性名}
如果使用的事务方式为 jdbc,则需要手动提交:sqlSession.commit()

Mybatis动态代理方式的CRUD

原则:约定优于配置

硬编码方式:abc.java
Configguration conf = new Configuration();
con.setName(“myProject”);
配置方式:abc.xml
< name>myProject< /name>
约定:默认值就是myProject

具体实现步骤:

  1. jar包,config.xml ,mapper.xml…
  • (不同处)约定的目标:省略掉Statement,即根据约定直接定位出SQL语句

    • 1.建立一个接口PersonMapper

      • 约定:
        1. 方法名和mapper.xml 文件中标签的id值一样
        1. 方法的输入参数类型和parameterType一样
        1. 方法的返回值和resultType一样
      • 如: Person queryPersonById(int id);
    • 要实现接口中的方法 和 Mapper.xml中的SQL标签一 一对应,还需要:

      • namespace的值,就是接口的全类名(接口–mapper.xml一 一对应)

匹配的过程:(约定过程)

  1. 根据接口名 找到mapper.xml文件 (根据的是namespace=接口全类名)

  2. 根据接口的方法名 找到mapper.xml文件中的SQL标签(方法名=SQL标签id值)

    根据以上两点可以保证:当调用接口中的方法时,程序能自动定位到某一个Mapper.xml文件中的SQL标签

习惯:SQL映射文件 和 接口放在同一位置(注意修改config.xml中加载mapper.xml文件的路径)

以上,可以通过接口的方法————> 映射到SQL语句

 // 通过约定接口的方式
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
personMapper.updatePerson(person);      //  根据接口的方法 --->  自动查找到SQL语句

优化:

  1. 可以将数据库信息放入db.properties,再通过 ${key} 动态引入
<configuration>
    <properties resource="db.properties" />
  1. Mybatis 全局参数

    //开启缓存
    <settings>
        <setting name="cacheEnabled" value="true" />
        <setting name="" value="" />
        ...
    </settings>
  2. 设置类的别名(忽略大小写)

    • a. 设置单个别名

      在config.xml中,< typeAliases> < typeAlias type=”org…Person” alias=”Person” />…

    • b. 批量设置别名,别名就是不带包名的类名

      // 包名
      < typeAliases> < package name=”org.test” />…
      Mybatis 还内置了一些常见类的别名:_integer/_byte/_long/int/boolean…

  3. 类型处理器 (类型转换器)(将Java类型转换为JDBC类型,如int转换为oracle的number类型)

    • a. Mybatis 自带一些常见的类型处理器

      如:BooleanTypeHandler:Boolean/boolean–>任何兼容的布尔值
      IntegerTypeHandler:Integer/int–>任何兼容的数字和整形

    • b. 自定义Mybatis 类型处理器

      Java类型 –> 数据库(JDBC类型)
      示例:

      实体类Student: boolean  stuSex  true:男 false: 女  
      表student:number  stuSex 1:男  0:女

自定义类型转换器(boolean – number) 步骤:

a. 创建类型转换器,要实现 TypeHandler接口 或继承BaseTypeHandler(更简单)
b. 在config.xml 中配置转换器

<!-- 类型处理器,将java的boolean类型 转换为 jdbc的number类型 -->
<typeHandlers>
    <typeHandler handler="com.mybatis.MyTypeHandler" javaType="Boolean" jdbcType="INTEGER" />
</typeHandlers>

c. 更改映射文件

 <!-- 验证类型转换器
       1. 如果类的属性 和 表的字段类型 能够合理识别(String - varchar2),则可以使用resultType,否则使用resultMap(boolean-number)
       2.    如果类的属性名 和 表的字段名 能够合理识别(String - varchar2),则可以使用resultType,否则使用resultMap(boolean-number)
-->
<select id="queryPersonByIdWithConverter" resultMap="resultPerson" parameterType="int">select * from person where id = #{id}</select>
<!-- 建立Person属性和表的字段的对应关系  主键id,非主键result , 再设置需要转换的类型-->
<resultMap id="resultPerson" type="person">
    <id property="id" column="id" />
    <result property="sex" column="sex" javaType="boolean" jdbcType="INTEGER" />
</resultMap>

类型转换的增加

// 更改SQL语句中需要转换的字段
#{sex,javaType=boolean,jdbcType=INTEGER}

注意:

1. 映射文件路径

<!-- 在配置文件中加载映射文件 IDEA里无法加载src下的xml文件 因此改用url class package等方式(接口与映射文件名称相同),
       或将xml文件放到resources目录-->
<!-- 通过包名设置路径 -->
<package name="com.mybatis.myInter" />

2. 映射文件和config.xml的jdbcType的类型

integer 必须写成大写形式:INTEGER

5. 取值符号

  1. 简单类型
    #{任意值} // 自动给String类型加上单引号’’ (自动类型转换)
    ${value} // 原样输出,但适合于动态排序(动态字段),其中的标识符只能是value
  2. 对象类型
    #{属性名}
    ${属性名}

SSM整合

  1. SSM需要的jar

    • mybatis.jar
    • spring-tx.jar
    • spring-jdbc.jar
    • spring-context.support.jar
    • spring-core.jar
    • spring-beans.jar
    • spring-aop.jar
    • spring-web.jar
    • spring-expression.ar
    • spring-context.jar
    • commons-logging.jar
    • commons-dbcp.jar
    • ojdbc.jar
    • mybaits.jar
    • log4.jar
    • commons-pool.jar
  2. Mybatis需要的:类-表

  3. Mybaits配置文件config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-config.dtd">
    
    <configuration>
        <!-- 加载数据库配置文件 -->
        <properties resource="db.properties"/>
        <!-- 批量设置别名 -->
        <typeAliases>
            <package name="bean" />
        </typeAliases>
    
        <!-- 配置数据库环境 -->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 加载映射文件 -->
        <mappers>
            <package name="dao" />
        </mappers>
    </configuration>
  4. 通过mapper.xml将类、表建立映射关系

  5. 之前MyBatis使用config.xml –> SqlSessionFactory,现在整合时,需要通过Spring管理SqlSessionFactory,因此 产生SqlSessionFactory不再放入config.xml,而是放入spring配置文件

  1. 使用spring整合Mybatis

    目标: 通过spring产生mybatis最终操作需要的动态mapper对象
    spring产生动态mapper对象有3种方法:

    • DAO层实现类 继承 SqlSessionDaoSupport类:提供了一个属性 SqlSession ,产生mapper

    • 扫描包方式

  2. 继续整合SpringMVC:将springmvc加入项目即可:需要spring-webmvc.jar

    • 给项目加入springmvc支持:web.xml–> dispatcherServlet
    • 编写springmvc配置文件:applicationContext-controller.xml:视图解析器、注解驱动(基础配置、标配)