Spring4.5使用笔录

1、Spring启动需要的jar包

2、如果要使用Spring-MVC还需要jar包

3、如果要集成mybatis,需要以下jar包:

4、Spring各个jar包说明:

附:可以参考网址 http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/

 5、单独启动Spring配置文件

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>SpringDemo</display-name>
    
    <!-- 监听spring上下文容器 -->  
    <listener>  
        <listener-class>  
            org.springframework.web.context.ContextLoaderListener  
        </listener-class>  
    </listener>  
      
    <!-- 加载spring的xml配置文件到 spring的上下文容器中 -->  
    <context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath:config/spring-context*.xml</param-value> 
    </context-param> 
</web-app>

spring-context*.xml即是Spring的配置文件,*是通配符————Spring配置文件可以按用途分为多个

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
                http://www.springframework.org/schema/context  
                 http://www.springframework.org/schema/context/spring-context-3.2.xsd  
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <!-- 导入自定义配置文件(交给Spring管理) -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:config/jdbc.properties</value>
            </list>
        </property>
    </bean>
    
    <!-- 导入自定义配置文件(交给Spring管理) -->
    <context:property-placeholder location="classpath:config/jdbc.properties" />
</beans>

两种导入自定义配置文件的方式任选一种即可。

 6、Spring集成Spring-MVC配置文件

在Spring基础上web.xml中增加

<!-- SpringMVC的请求都统一由DispatcherServlet处理 -->
  <servlet>
    <servlet-name>handler</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:config/springmvc-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!-- SpringMVC拦截的请求类型 -->
  <servlet-mapping>
    <servlet-name>handler</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
  <!-- 过滤UTF-8以外的字符集 -->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

springmvc-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd  
                http://www.springframework.org/schema/aop  
                http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置包扫描,配置之后SpringMVC和Spring都能注解了 -->
    <context:component-scan base-package="test.mvc.*" />
    
</beans:beans>  

SpringMVC配置文件中设置了包扫描后,Spring的配置文件可以不用设置

7、Spring集成Mybatis

Spring配置文件增加数据源和会话工厂

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
                http://www.springframework.org/schema/context  
                 http://www.springframework.org/schema/context/spring-context-3.2.xsd  
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <!-- 导入自定义配置文件(交给Spring管理) -->
    <context:property-placeholder location="classpath:config/jdbc.properties" />

    <!-- 数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="initialSize" value="${pool.initialSize}" />
        <property name="maxActive" value="${pool.maxActive}" />
        <property name="maxIdle" value="${pool.maxIdle}" />
        <property name="minIdle" value="${pool.minIdle}" />
    </bean>

    <!-- 使用mybatis方式持久化数据 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:config/mybatis-config.xml"/>
        <!-- 当mybatis的xml文件和mapper接口不在相同包下时,需要用mapperLocations属性指定xml文件的路径。 *是个通配符,代表所有的文件 -->
        <property name="mapperLocations" value="classpath:com/czgps/ssm/mybatis/*.xml" />
    </bean>

    <!-- mybatis批量扫描,扫描出mapper接口自动创建代理对象并在Spring容器中注册 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- mapper.java和mapper.xml文件名称保持一致且在同一目录中,扫描出的mapper对应的bean的id是类型名(首字母必须小写) -->
        <property name="basePackage" value="com.czgps.ssm.mapper" />
        <!-- Spring整合Mybatis是会出现加载jdbc参数异常(因为配置文件加载问题) -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>
</beans> 

mybatis配置文件mybatis-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>
    <typeAliases>
        <!-- 为com.czgps.ssm.po包下的类取别名,别名就是类型名(首字母大小写均可) -->
        <package name="com.czgps.ssm.po" />
    </typeAliases>
</configuration>

注意:mybatis延迟加载特性依赖于cglib,而cglib依赖asm,所以要导入cglib和asm两个jar包,jar包下载:https://github.com/mybatis/mybatis-3/releases

使用log4j在控制台输出日志,方便debug导入log4j的jar包和commons-logging,slf4j是日志抽象层

如果mybatis要支持分页查询,可以使用分页插件,jar包下载:http://git.oschina.net/free/Mybatis_PageHelper/blob/master/wikis/HowToUse.markdown

同时在mybatis-config.xml中增加插件拦截

<plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!-- 该参数默认为false -->
        <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
        <!-- 和startPage中的pageNum效果一样-->
        <property name="offsetAsPageNum" value="true"/>
        <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
        <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
        <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
        <property name="reasonable" value="true"/>
        <!-- 支持通过Mapper接口参数来传递分页参数 -->
        <property name="supportMethodsArguments" value="false"/>
        <!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
        <property name="returnPageInfo" value="none"/>
    </plugin>
    </plugins>

如果要支持事务,在spring-context.xml中增加事务配置;现在一般使用声明式注解@Transactional ,配置如下:

    <!-- 事务管理 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 使用annotation定义事务 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

如果使用配置文件配置方式使用事务(不推荐),配置如下;主要是先定义一个增强,然后为这个增强配置切点;这种的缺点是为方法配置事务策略时不够灵活需要更改配置文件,注解就没有这种限制。

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

    <!-- 增强 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 事务传播行为propagation默认REQUIRED / 事务隔离级别isolation默认DEFAULT -->
        <tx:attributes>
            <tx:method name="*" rollback-for="java.lang.Exception" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
        </tx:attributes>
    </tx:advice>

    <!-- aop切点 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* czgps.app.service.*.*(..))" />
    </aop:config>

运行期(unchecked)异常RuntimeException默认回滚,编译时(checked)异常默认不会回滚
unchecked异常如果不想回滚就用noRollbackFor来指定,对子类有效
checked异常如果需要回滚就用rollbackFor来指定,对子类有效

详细参考如下:

注意的几点:
1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.

2用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");

}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}

3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。

5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

8、以上使用到的jdbc.properties内容

###数据库连接池设置########
pool.initialSize=5
pool.maxActive=10
pool.maxIdle=5
pool.minIdle=1
pool.maxWait=1000
pool.poolPreparedStatements=true
pool.defaultAutoCommit=false

###########数据库连接设置##########
#驱动类
jdbc.driver=com.mysql.jdbc.Driver
#连接地址
jdbc.url=jdbc:mysql://192.168.1.100:3306/Study?useUnicode=true&characterEncoding=utf8
#用户名
jdbc.username=root
#密码
jdbc.password=root

9、使用slf4j日志替代log4j(桥接)

jcl-over-slf4j-1.7.22.jar用于替代替代common-logging.jar(这就是桥接的作用,相当于冒名顶替,有一个包名结构几乎一样的jar包替换掉common-logging.jar)
如下是几个依赖包:

    <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.22</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.22</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.8</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.8</version>
        </dependency>

10、Spring决策代理类型(Cglib和JDK)

Spring优先采用JDK动态代理(有接口的前提),没有接口只能用cglib
ProxyProcessorSupport类的evaluateProxyInterfaces方法与DefaultAopProxyFactory类中createAopProxy方法一起决策Spring的代理
<aop:aspectj-autoproxy proxy-target-class="true"/>表示强制使用cglib,如果不要proxy-target-class则Spring自己会在JDK代理与cglib间切换
<aop:aspectj-autoproxy/>在Spring和SpringMVC的配置文件中分别配置
@Service这样的注解要放在实现类上,不是放在接口上(代理的是目标类,不是它实现的接口)

ProxyProcessorSupport类的evaluateProxyInterfaces方法可以看出,如果目标类没有实现接口直接就是Cglib代理

    protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
        // 获取接口的所有父接口;递归查找包括直接和间接实现的接口
        Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
        boolean hasReasonableProxyInterface = false;
        for (Class<?> ifc : targetInterfaces) {
            if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
                    ifc.getMethods().length > 0) {
                hasReasonableProxyInterface = true;
                break;
            }
        }
        if (hasReasonableProxyInterface) {
            // Must allow for introductions; can't just set interfaces to the target's interfaces only.
            for (Class<?> ifc : targetInterfaces) {
                proxyFactory.addInterface(ifc);
            }
        }
        else {
            // 没有实现任何接口,直接就走Cglib
            proxyFactory.setProxyTargetClass(true);
        }
    }

DefaultAopProxyFactory类中createAopProxy方法,最终决定生成的AopProxy是Cglib还是JDK的

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 1、config.isOptimize()基本类型
        // 2、config.isProxyTargetClass()表示已经判断出要使用Cglib
        // 3、用户没有提供任何实现的接口
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            
            // 如果目标类是接口或者Proxy类,也默认走JDK代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

11、SpringMVC返回值问题

1、 返回ModleAndView,表示直接返回一个视图
2、 自定义模型(json),一般做为数据接口,需要@ResponseBody注解——表示将序列化出的json写入到响应流的响应体中
3、 返回值类型为String类型的逻辑视图名称(带有forward或redirect前缀的视图名称后必须是完整的资源路径)

往请求对象中添加属性键值对的三种方式
1、HttpServletRequest的setAttribute
2、Model的addAttribute
3、ModelAndView的addObject

带有forward或redirect前缀的逻辑视图名
1、forward只能调转当前web应用内的资源,其实框架内部就是调用的RequestDispatcher.forward
2、redirect可以调转其他web应用的资源(使用http://url格式的绝对路径),框架内部调用response.sendRedirect

SpringMVC框架返回值有专门的处理器,其对应关系是:
  ModleAndView——ModelAndViewMethodReturnValueHandler
  String(逻辑视图名)——ViewNameMethodReturnValueHandler
  带@ResponseBody注解的自定义模型(json)——RequestResponseBodyMethodProcessor
每个处理器都实现了HandlerMethodReturnValueHandler并重写了handleReturnValue方法,该方法就是用于处理返回值的。

 

查看ViewNameMethodReturnValueHandler的handleReturnValue方法可以看到,直接返回String时,返回值直接作为了视图名称

在对逻辑视图名url进行解析时,会处理forward和redirect前缀;只有不带forward或者redirect前缀的视图才会拼接视图解析器配置的前后缀,
查看UrlBasedViewResolver类的createView方法如下

结合上图可以看到super.createView最终会调用UrlBasedViewResolver类的buildView来构建视图,buildView中读取了视图解析器的前后缀

附带测试项目:https://files.cnblogs.com/files/xy-nb/springmvc-total.zip

12、占位

posted @ 2017-04-14 15:40  晨见虹  阅读(834)  评论(0)    收藏  举报