spring 入门

91 阅读35分钟

Spring

Spring 基础

spring特性

非侵入式:在使用spring开发程序时,spring对应用程序的结构影响非常小,对领域模型可以做到零污染,对功能性组件也只需要使用几个简单的注解进行标记,不会破坏原有的结构。

IOC:控制反转,反转资源方向,把自己创建资源,向环境索取资源变成环境将资源准备好。

AOP:面向切面编程,在不修改源代码的基础上增加代码功能。

容器:spring IOC是一个容器,因为他包含并且管理组件对象的生命周期。组件使用容器化管理,替程序员屏蔽了组件创建过程中大量的细节。

组件化:spring实现了简单的组件配置组合成一个复杂的应用,可以使用XML和JAVA注解组合这些对象,可以基于一个个功能明确、边界清洗的组件有条不紊的搭建大型复杂应用系统。

声明式:很多功能只需要声明需求,即可由框架代为实现。

一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和第三方类库。

依赖注入(DI)和控制反转(IOC)

通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。用来降低代码之间的耦合度。

在没使用ioc之前我们的代码都是由程序员硬编码,对象A依赖于对象B时,我们都需要每次都new一个对象B或者使用以及new出来的对象B,如此的弊端是B对象有修改则A依赖B的部分也需要修改。

而使用IOC之后,我们不在需要自己new一个新对象,所有的对象都由Spring控制,即控制反转

IOC(控制反转)

IOC的思想基于IOC容器,本质上就是利用工厂模式创造出需要的对象(也就是本来由程序员管理的对象,交给spring 管理)。

Spring提供了两个接口来实现IOC:

  • Beanfactory:IOC容器的基本实现,是Spring内部使用,开发中一般不使用

    • 加载配置文件的时候不会创建对象,只有在获取对象的时候才会创建对象。
  • ApplicationContext:是Beanfactory的子接口,提供了更多功能,面向开发人员使用。

    • 在加载配置文件的时候就会把配置文件中的对象进行创建
  • ApplicationContext 的实现类

  • IOC操作Bean管理

    • 创建对象
    • 注入属性
  • bean管理的两种方式

    • 基于xml配置文件实现
    • 基于注解实现

DI依赖注入

本质就是注入属性,是IOC的一种具体实现

使用xml进行管理对象

  • 在Spring配置文件中,使用bean标签,在标签里添加对应的属性或子节点,就可以实现对象的创建和注入
  • 在创建对象的时候,默认是使用无参的构造函数创建对象
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd>
<bean id="xx" class="xx.xx"></bean>
</beans>

使用set注入

<!--  ID是注入的对象,class是全类名  -->
<bean id="person" class="com.test.a.Person" >
    <!--    property是注入的属性,name是属性名,value是属性值    -->
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
</bean>

使用有参构造注入

<bean id="person" class="com.test.a.Person" >
    <!--    constructor-arg使用构造注入,与property基本一致    -->
    <constructor-arg name="age" value="22"/>
    <constructor-arg name="name" value="zs"/>
</bean><bean id="person" class="com.test.a.Person" >
    <!--    使用构造索引来写,一般不用   -->
    <constructor-arg index="0" value="22" />
    <constructor-arg index="1" value="zs" />
</bean>

P名称空间注入(使用不多)

使用名称空间注入,可以简化xml的配置方式,底层还是用的set方法注入

<!-- 使用步骤 -->
<!--  1.在根节点beans的属性中添加下面内容
    xmlns:p="http://www.springframework.org/schema/p" -->
<!-- 2.直接在bean的属性中使用p:属性名=“属性值” -->
<bean id="person" class="com.test.a.Person" p:name="zs" p:age="22"></bean>

注入null和特殊符号

<!-- 注入空值,在property中不写value属性,并添加null子标签-->
<bean id="person1" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age">
        <null/>
    </property>
</bean>
<!-- 注入特殊符号-->
<bean id="person1" class="com.test.a.Person" >
    <!--  <![CDATA[^&*(^$]]>在value中添加<![CDATA[]]>里面的内容将原样注入 -->
    <property name="name">
        <value><![CDATA[^&*(^$]]></value>
    </property>
    <property name="age" value="22"/>
</bean>

注入bean(外部注入)

<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
<!--    ref注入其他bean   -->
    <property name="pet" ref="dog" />
</bean>
<bean id="dog" class="com.test.a.Dog"></bean>

内部bean

<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
    <property name="pet">
        <!--     内部直接嵌套一个bean    -->
        <bean id="dog" class="com.test.a.Dog"></bean>
    </property>
</bean>

注入数组

<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
    <property name="toy">
        <!-- 可以使用array或list注入数组 -->
        <array>
            <value>高达</value>
            <value>变形金刚</value>
        </array>
    </property>
</bean>

list内部注入

<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
    <property name="toys">
        <list>
            <value>高达</value>
            <value>变形金刚</value>
        </list>
    </property>
</bean>

注入map

<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
    <property name="girlFriends">
        <!--            map类型使用map内部嵌套子标签entry使用-->
        <map>
            <entry key="name" value="zs"/>
        </map>
    </property>
</bean>

注入list(对象)

<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
    <property name="dogs">
        <list>
            <!-- 使用ref指定对象 -->
            <ref bean="dog1"></ref>
            <ref bean="dog2"></ref>
        </list>
    </property>
</bean>
<bean id="dog1" class="com.test.a.Dog"/>
<bean id="dog2" class="com.test.a.Dog"/>

list/map集合bean抽取(单独写)

<!-- 1.添加beans中添加命名空间 -->
<xmlns:util="http://www.springframework.org/schema/util"
xsi:http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 2.添加util:list标签,把list内容放进去,util是约束 -->
<util:list id="dog_list">
    <ref bean="dog1"></ref>
    <ref bean="dog2"></ref>
</util:list>
<bean id="person" class="com.test.a.Person" >
    <property name="name" value="zs"/>
    <property name="age" value="22"/>
    <property name="dogs" ref="dog_list"/>
</bean>
<bean id="dog1" class="com.test.a.Dog"/>
<bean id="dog2" class="com.test.a.Dog"/>
<!-- map -->
<util:map id="map1">
    <entry key="123" value-ref=""
</util:map>

bean的常用属性详解和子节点

  • id和name属性的区别:

    • 作用基本没有区别主要用于识别bean对象,推荐使用id。
    • name取值比较随意,甚至可以用数字开头。
    • 在配置文件中允许出现两个name相同的bean,在用getBean()返回实例时,后面一个Bean被返回。
    • 如果没有定义id,name,则用类的全名作为name,如,可以使用getBean("test.Test")返回该实例。
  • lazy-init:懒加载,

    • default=false
    • 普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean(“”)时进行触发。
    • Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap里供下面的初始化时用。接下来对每个BeanDefinition进行处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进行初始化并依赖注入。
  • class: 用于指定类的路径

    • 使用全类名,来指定对应的类

bean的子节点

    • 属性注入利用反射set来给属性赋值 //使用前必须有对应的set方法
    • name:参数名
    • value:参数值
    • ref:引用配置文件中的对象进行赋值
    • 构造器注入,利用反射使用对应的构造器进行注入 //使用前必须有对应的构造器
    • name:参数名
    • type: 属性类型(使用全类名)
    • index:下标
    • value:参数值
    • name、index、type三选一,一般推荐使用name
    • 数组注入,在array里使用value来进行多个值注入
    <property name="books">
        <array>
            <value>百年孤独</value>
            <value>城市发展史</value>
            <value>美国大城市的死与生</value>
        </array>
    </property>
    
  • 列表注入

    <property name="hobbies">
        <list>
            <value>打游戏</value>
            <value>听歌</value>
            <value>看电影</value>
        </list>
    </property>
    
  • 键值对注入

    <property name="grade">
        <map>
            <entry key="history" value="100"/>
            <entry key="math" value="99"/>
        </map>
    </property>
    
  • 集合注入

    <property name="games">
        <set>
            <value>MSG</value>
            <value>LOL</value>
            <value>CF</value>
        </set>
    </property>
    
  • 空值注入

    一般情况下设置空值只需要把value置空即可

    <!-- 设置空值 -->
    <property name="wife" value=""/><!-- 设置null值 -->
    <property name="wife" >
        <null/>
    </property>
    
  • 配置文件注入

    <property name="prop">
        <props> <!-- 属性名用key,属性值则直接用对应的值 -->
            <prop key="driver">com.mysql.jdbc.Driver</prop>
            <prop key="url">jdbc:mysql://localhost:3306/test</prop>
            <prop key="username">root</prop>
            <prop key="password">root</prop>
        </props>
    </property>
    

其他标签

别名 可以使用原名称以外的名称

<alias name="person" alias="person2"/>
//name是原名称 alias是别名
<import resource="beans1.xml"/>
//导入其他bean的配置文件

简写 和

使用简写的前提是需要添加命名空间

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
<!-- p简写set注入 -->
<bean class="software.yibai.pojo.EduBackground" 
p:university="beijing_university"
p:middle_school="beijingwaiguoyu_middle_school"></bean><!-- c简写构造注入 -->
<bean class="software.yibai.pojo.EduBackground" c:middle_school="beijingwaiguoyu_middle_school"
c:university="beijing_university"></bean>

自动装配

  1. 配置文件自动装配

    • autowire:配置spring对象属性的默认的装配方式
    • 自动装配:不主动编写对应的参数值,spring框架会根据你的装配模式自动把对应的属性值装配上
    • no: 默认值 不启用自动装配
    • byType :根据类型自动装配,byType和byName都会在spring上下文中自动查找对应的对象,使用byType中,在IOC中容器中只能有一个对应的类型。
    • byName: 根据类的属性名自动装配,也就是类的属性名称和IOC容器中对象的名称一致。
<bean class="software.yibai.pojo.EduBackground" id="school_">
    <property name="university" value="beijing_university"/>
    <property name="middle_school" value="beijingwaiguoyu_middle_school"/>
</bean>

<bean class="software.yibai.pojo.Person" name="person" autowire="byType"></bean>
<!-- 使用bytype后spring框架会自动根据Person属性对应的对象类型装配进Person中 -->
<!-- 使用byName同理 -->
  1. 注解自动装配

使用前提:添加命名空间和配置//自动装配只适用于引用类型,八个基础类型不支持

<!-- 添加命名空间 -->
xmlns:context="http://www.springframework.org/schema/context" 
<!-- 添加配置 -->
<!-- idea中只要输入下面的配置会自动导入命名空间 -->
<context:annotation-config/>

bean管理

Spring有两种类型的bean,一种是普通的bean,另一种是工程bean(FactoryBean)

  • 普通的bean:在配置文件中定义的bean类型就是返回的类型
  • 工厂bean:在配置文件中定义的bean可以和返回类型不一样

工厂bean

xml返回FactoryBean,然后实现FactoryBean接口即可使用工厂bean,让工厂生产对象进行返回

public class MyFactoryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return null;
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    @Override
    public boolean isSingleton() {
        return false;
    }
}

bean的作用域

在bean标签内添加scope属性来改变bean的作用域

scope:bean的作用域

  • singleton 默认值 单例模式

  • prototype 原型模式 多例模式

  • 单例和多例的区别

    • singleton在加载配置文件的时候就会创建单例对象
    • prototype只有在getbean的时候才会创建多例对象
  • session:创建对象,把对象放到session域里面

  • request:创建对象,把对象放到request域里面

bean的生命周期简易版

1.实例化对象

2.注入属性(set注入)

3.手动init的方法(在xml的bean设置属性init-method)

4.销毁对象(在xml的bean设置属性destory-method)对象只会在ioc容器关闭时才会销毁

bean的后置处理器

bean的后置处理器会在bean的生命周期初始化前后添加额外的操作,需要实现Beanpostprocessor接口,且配置到IOC容器中,需要注意的是,bean的后置处理器会对IOC生成的所有bean生效。

注解管理spring

通过注解+扫描配置的bean的默认ID,默认是类的小驼峰。即类名首字母小写

如果要更改默认的beanID可以在注解中加入自定义的名称即可修改默认ID

<!-- 使用注解要先扫描包 -->
<context:component-scan base-package="edu.mk.mkmusic">
    <!-- 不扫描的内容 -->
	<context:exclude-filter type="匹配模式" expression="edu.mk.controller"></context:exclude-filter>
    <!-- 只扫描的内容 -->
	<context:include-filter type=""></context:include-filter>
</context:component-scan>

注解自动装配

在对应的属性上添加注解@autowired

@autowired可以标识的位置

a、成员变量

b、set方法

c、有参构造

@autowired注解原理

默认通过byType方法,在IOC容器中匹配bean进行赋值,如果存在同一个类型多个类的话则使用byName进行匹配,如果都无法匹配,则使用@Qualifier注解进行指定ID

AOP (面向切面编程)

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面

向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况

下给程序动态统一添加额外功能的一种技术。

作用

简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,

提高内聚性。

代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就

被切面给增强了。

动态代理

特点:字节码随用随创建,随用随加载 作用:不修改源码的基础上对方法增强 分类: 基于接口的动态代理 基于子类的动态代理 基于接口的动态代理: 涉及的类:Proxy 提供者:JDK官方 如何创建代理对象: 使用Proxy类中的newProxyInstance方法 创建代理对象的要求: 被代理类最少实现一个接口,如果没有则不能使用 newProxyInstance方法的参数: ClassLoader:类加载器 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。 Class[]:字节码数组 它是用于让代理对象和被代理对象有相同方法。固定写法。 InvocationHandler:用于提供增强的代码 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。 此接口的实现类都是谁用谁写。

相关术语

横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

通知(Advice)

每一个横切关注点上要做的事情都需要写一个方法来实现,这就叫通知方法

  • 前置通知:使用@Before注解标识,在被代理的目标方法执行

  • 返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行(寿终正寝

  • 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(死于非命

  • 后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行(盖棺定论

  • 环绕通知:使用@Around注解标识,使用try...catch...finally结构围绕整个被代理的目标方法,包

    括上面四种通知对应的所有位置

切面(Aspect)

封装了通知方法的类。

目标(Target Object)

被代理的目标对象

代理

将切面应用到目标对象并导致代理对象创建的过程。

连接点(JoinPoint)

程序执行过程中明确的点,一般是方法的调用,被拦截到的点。因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

切入点

定位连接点的方式。

每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。

如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。

Spring 的 AOP 技术可以通过切入点定位到特定的连接点。

切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条

件。

AOP使用

面向切面的组件类需要添加@Aspect注解

切面方法需要添加注解@Before、

切入点表达式:设置在标识通知的注解的value属性中

例:@Before("execution(public int edu.mk.music.PersonContorl.方法名(参数))")

语法细节

用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限

在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。

例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello

在包名的部分,使用“*..”表示包名任意、包的层次深度任意

在类名的部分,类名部分整体用*号代替,表示类名任意

在类名的部分,可以使用*号代替类名的一部分

例如:*Service匹配所有名称以Service结尾的类或接口

在方法名部分,可以使用*号表示方法名任意

在方法名部分,可以使用*号代替方法名的一部分

例如:*Operation匹配所有方法名以Operation结尾的方法

在方法参数列表部分,使用(..)表示参数列表任意

在方法参数列表部分,使用(int,..)表示参数列表以一个int类型的参数开头

在方法参数列表部分,基本数据类型和对应的包装类型是不一样的

切入点表达式中使用 int 和实际方法中 Integer 是不匹配的

在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符

例如:execution(public int .. Service.*(.., int)) 正确

例如:execution(* int .. Service.*(.., int)) 错误

//获取连接点所对应方法的签名信息
@Before("execution(* edu.mk.music.*.*(..))")
public void beforeMethod(JoinPoint joinPoint){ 
    //获取切入的方法名
    String methodName = joinPoint.getSignature().getName(); 
    //获取切入的方法参数
    String args = Arrays.toString(joinPoint.getArgs()); 
    System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args); 
}
重用切入点表达式
//声明要被复用的切入方法
@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))") 
public void pointCut(){}

//同一个切面中使用
@Before("pointCut()") 
public void beforeMethod(JoinPoint joinPoint){ 
    String methodName = joinPoint.getSignature().getName(); 
    String args = Arrays.toString(joinPoint.getArgs()); 
    System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args); 
}

//不同切面中使用
@Before("com.atguigu.aop.CommonPointCut.pointCut()") 
public void beforeMethod(JoinPoint joinPoint){ 
    String methodName = joinPoint.getSignature().getName(); 
    String args = Arrays.toString(joinPoint.getArgs()); 
    System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args); 
}
获取切入点目标的返回值
//AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值
@AfterReturning(value = "execution(public int edu.mk.music.PersonControl (..))", returning = "result") 
public void afterReturningMethod(JoinPoint joinPoint, Object result){ 
    String methodName = joinPoint.getSignature().getName(); 
    System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result); 
}
获取切入点目标的异常
//AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值
@AfterThrowing(value = "execution(public int edu.mk.music.PersonControl (..))", throwing = "ex") 
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){ 
    String methodName = joinPoint.getSignature().getName(); 
    System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex); 
}
环绕通知
@Around(value = "execution(public int edu.mk.music.PersonControl (..))") 
public void afterThrowingMethod(ProceedingJoinPoint joinPoint){ 
    String methodName = joinPoint.getSignature().getName(); 
    String args = Arrays.toString(joinPoint.getArgs()); 
    Object result = null; 
    try {
        System.out.println("环绕通知-->目标对象方法执行之前"); 
        //目标方法的执行,目标方法的返回值一定要返回给外界调用者 
        result = joinPoint.proceed(); 
        System.out.println("环绕通知-->目标对象方法返回值之后"); 
    } catch (Throwable throwable) { 
        throwable.printStackTrace(); 
        System.out.println("环绕通知-->目标对象方法出现异常时"); 
    } finally { 
        System.out.println("环绕通知-->目标对象方法执行完毕"); 
    }
    return result;
}
切面的优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用@Order注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低

事务

编程式事务

事务功能的相关操作全部通过自己编写代码实现

声明式事务

事务控制的代码结构是基本确定的,框架将固定模式的代码抽取了出来,进行相关的封装,我们只需要在配置文件中进行简单的配置即可完成声明式事务。

Spring事务管理器

开启事务注解驱动后,将使用@Transactionnal注解所标识的方法或类中所有的方法使用事务进行管理

<!-- 配置数据源 -->
<bean id="transactionManager" class="DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean><!-- 
    开启事务的注解驱动 
    通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务 -->
<!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就 是这个默认值,则可以省略这个属性 -->
<tx:annotation-drivern transaction-manager="transactionManager"/>
事务属性
只读

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这

样数据库就能够针对查询操作来进行优化。

@Transactional(readOnly = true)

对增删改操作设置只读会抛出下面异常:

Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification

are not allowed

超时

事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间

占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。

此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常

程序可以执行。

@Transactional(timeout = 3)

回滚策略

声明式事务默认只针对运行时异常回滚,编译时异常不回滚。

可以通过@Transactional中相关属性设置回滚策略

  • rollbackFor属性:需要设置一个Class类型的对象
  • rollbackForClassName属性:需要设置一个字符串类型的全类名
  • noRollbackFor属性:需要设置一个Class类型的对象
  • rollbackFor属性:需要设置一个字符串类型的全类名

@Transactional(noRollbackFor = ArithmeticException.class)

事务的传播

事务的传播机制是Spring框架实现的功能,是java层面的概念。

事务的传播指的是,一个事务方法A,被另外一个方法B调用的时候,对方法A有何种影响(两个方法事务独立执行、A方法事务合并到B方法事务、或以非事务方式执行等)。

Spring声明式事务的实现原理默认采用动态代理的方式实现,在事务方法的前面动态切入开启事务的代码,在事务方法结束动态切入提交/回滚事务的代码。

事务的传播特性就是根据propagation属性配置的差异,在事务方法被调用时是否需要在本方法中切入 开始|提交|回滚 事务的代码。

SpringMVC

什么是MVC

MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分

M:Model,模型层,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

一类称为实体类Bean:专门存储业务数据的,如 Student、User 等

一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。

V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller

调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果

找到相应的View视图,渲染数据后最终响应给浏览器

Spring特点

Spring 家族原生产品,与 IOC 容器等基础设施无缝对接

基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一

处理

表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案

代码清新简洁,大幅度提升开发效率

内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可

性能卓著,尤其适合现代大型、超大型互联网项目要求

Idea创建Web项目

正常创建完web项目后,修改POM文件,把当前项目的打包方式修改为war。然后进入Project Structure 找到Modules,点击当前项目,选择Deployment Descriptors 添加web.xml修改路径为src\main\webapp\WEB-INF\web.xml即可完成,下面的web resources Directory会自动修改路径,如果没修改修改为src\main\webapp即可。

配置web.xml

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
        <init-param>
            <!-- contextConfigLocation为固定值 -->
            <param-name>contextConfigLocation</param-name>
            <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的 src/main/resources -->
            <!-- classpath:指的是resources目录,如果下面的配置文件找不到则无法启动springioc容器 -->
            <param-value>classpath:SpringMVC-Servlet.xml</param-value>
        </init-param>
        <!--作为框架的核心组件,
        在启动过程中有大量的初始化操作要做
        而这些操作放在第一次请求时才执行会严重影响访问速度
        因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <!--设置springMVC的核心控制器所能处理的请求的请求路径
         /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
          但是/不能匹配.jsp请求路径的请求 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

请求控制器

由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要

创建处理具体请求的类,即请求控制器

请求控制器中每一个处理请求的方法成为控制器方法

因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识

为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在

SpringMVC注解

@RequestMapping

@RequestMapping注解:处理请求和控制器方法之间的映射关系 ,注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径

@requestMapping设置在类上表示的是本类访问的初始路径,如果设置在方法上则是在类的基础上增加方法的路径

在@requestMapping注解的基础上,派生出了@getMapping,@postMapping,@putMapping,@deleteMapping等注解,使用这些注解可以省去method属性的设置,直接对应请求所需要的method。

params属性,请求参数匹配请求,即要求浏览器请求映射所匹配的请求必须携带param请求的参数

@headers属性,若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误。

@RequestParam、@RequestHeader、@CookieValue

三个属性都是将浏览器发送过来的请求参数名与方法参数名进行映射

@RequestParam是将请求参数和控制器方法的形参创建映射关系

@RequestParam注解一共有三个属性:

value:指定为形参赋值的请求参数的参数名

required:设置是否必须传输此请求参数,默认值为true

若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置

defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为

false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为

null

defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值

为""时,则使用默认值为形参赋值

@RequestMapping("/getParams")
public void getParams(@RequestParam("param") String param){
    System.out.println(param);
}
@RequestBody

用于接收Ajax请求,使用request.getparamater无法获取到ajax的请求信息所以使用requestBody进行ajax的请求处理。

postman模拟Ajax请求

先在请求头中添加以下内容:

Content-Type:application/x-www-form-urlencoded

X-Requested-With:xmlhttprequest

然后在body中选择x-www-form-urlencoded,参数为模拟AJAX提交的内容

即可

@RequestMapping(value = "/ajax")
// 如果不设置required = false那么直接访问改接口会导致400错误,因为无法正确匹配到ajax的信息
public void AjaxTest(@RequestBody(required = false) String requestBody,HttpServletResponse response) throws IOException {
    System.out.println(requestBody);
}
@ResponseBody

使用之后会把返回的对象转换为json发送给浏览器

服务器处理ajax请求之后,大多数情况都需要向浏览器响应一个java对象,此时必须将java对象转换为

json字符串才可以响应到浏览器,之前我们使用操作json数据的jar包gson或jackson将java对象转换为

json字符串。在SpringMVC中,我们可以直接使用@ResponseBody注解实现此功能

使用@ResponseBody注解标识控制器方法,在方法中,将需要转换为json字符串并响应到浏览器

的java对象作为控制器方法的返回值,此时SpringMVC就可以将此对象直接转换为json字符串并响应到

浏览器

@RequestMapping("/testResponseBody") 
@ResponseBody 
public String testResponseBody(){ 
    //此时响应浏览器数据success 
    return "success"; 
}
@RestController注解

复合注解,标识在控制器的类上,相当于使用了Controller注解和ResponseBody注解

Ant风格的路径

?:表示任意的单个字符

*:表示任意的0个或多个字符

**:表示任意层数的任意目录

注意:在使用时,只能使用/ /xxx的方式

路径中的占位符

把请求的参数变成了请求的路径

原始方式:/deleteUser?id=1

rest方式:/user/delete/1

SpringMVC路径中的占位符常用于restful风格中,当请求路径中将某些数据通过路径的方式传输到服

务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在

通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参

@RequestMapping("/testRest/{id}/{username}") 
//@PathVariable 获取占位符的数值
public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){ 
    System.out.println("id:"+id+",username:"+username); return "success"; 
}

springMVC运行流程

浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器

DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,

将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的

控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会

被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视

图所对应页面

Spring获取参数

servletAPI获取请求参数

将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请

求的请求报文的对象

@RequestMapping("/testParam") 
public String testParam(HttpServletRequest request){ 
    String username = request.getParameter("username"); 
    String password = request.getParameter("password"); 
    System.out.println("username:"+username+",password:"+password); 
    return "success"; 
}

通过控制器方法的形参获取请求参数

在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在

DispatcherServlet中就会将请求参数赋值给相应的形参

即,只要前端请求的参数和java方法获取了对应的参数名即可获取数值。

注:

若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串

数组或者字符串类型的形参接收此请求参数

若使用字符串数组类型的形参,此参数的数组中包含了每一个数据

若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果

<a th:href="@{/testParam(username='admin',password=123456)}">测试获取请求参数-- >/testParam</a>
@RequestMapping("/testParam") 
public String testParam(String username, String password){ 
    System.out.println("username:"+username+",password:"+password); 
    return "success"; 
}

通过POJO获取请求参数

可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实

体类中的属性名一致,那么请求参数就会为此属性赋值

<form th:action="@{/testpojo}" method="post"> 
    用户名:<input type="text" name="username"><br> 
    密码:<input type="password" name="password"><br> 
    性别:<input type="radio" name="sex" value="男">男    
    <input type="radio" name="sex" value="女">女<br> 
    年龄:<input type="text" name="age"><br> 
    邮箱:<input type="text" name="email"><br> 
    <input type="submit"> 
</form>
@RequestMapping("/testpojo") public String testPOJO(User user){ 
    System.out.println(user); 
    return "success"; 
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='123@qq.com'}

SpringMVC过滤器解决乱码

解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是

必须在web.xml中进行注册

SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效

<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> 
    <init-param> 
        <param-name>forceEncoding</param-name> 
        <param-value>true</param-value> 
    </init-param> 
</filter> 
<filter-mapping> 
    <filter-name>CharacterEncodingFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping>

域对象共享数据

request域共享数据

使用ServletAPI向request域对象共享数据
@RequestMapping("/testServletAPI") 
public String testServletAPI(HttpServletRequest request){ 
    request.setAttribute("testScope", "hello,servletAPI"); 
    return "success"; 
}
使用ModelAndView向request域对象共享数据
@RequestMapping("/testModelAndView") 
public ModelAndView testModelAndView(){ 
    /**
    * ModelAndView有Model和View的功能 
    * Model主要用于向请求域共享数据 
    * View主要用于设置视图,实现页面跳转 */ 
    ModelAndView mav = new ModelAndView(); 
    //向请求域共享数据 
    mav.addObject("testScope", "hello,ModelAndView"); 
    //设置视图,实现页面跳转 
    mav.setViewName("success"); 
    return mav; 
}
使用Model、Map、ModelMap向request域对象共享数据

三者的关系:Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的

//Model共享数据
@RequestMapping("/testModel") 
public String testModel(Model model){ 
    model.addAttribute("testScope", "hello,Model"); 
    return "success"; 
}
//Map共享数据
@RequestMapping("/testMap") 
public String testMap(Map<String, Object> map){ 
    map.put("testScope", "hello,Map"); 
    return "success"; 
}
//ModelMap共享数据
@RequestMapping("/testModelMap") 
public String testModelMap(ModelMap modelMap){ 
    modelMap.addAttribute("testScope", "hello,ModelMap"); 
    return "success"; 
}

Session域共享数据

@RequestMapping("/testSession") 
public String testSession(HttpSession session){ 
    session.setAttribute("testSessionScope", "hello,session"); 
    return "success"; 
}

application域共享数据

@RequestMapping("/testApplication") 
public String testApplication(HttpSession session){ 
    ServletContext application = session.getServletContext(); 
    application.setAttribute("testApplicationScope", "hello,application"); 
    return "success"; 
}

SpringMVC的视图

SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户SpringMVC视图的种类很多,默认有转发视图和重定向视图

当工程引入jstl的依赖,转发视图会自动转换为JstlView

若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视

图解析器解析之后所得到的是ThymeleafView

ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置

的视图解析器解析,视图名称拼接视图前缀和视图

后缀所得到的最终路径,会通过转发的方式实现跳转

重定向视图

SpringMVC中默认的重定向视图是RedirectView

当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不

会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最

终路径通过重定向的方式实现跳转

注:

重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自

动拼接上下文路径

@RequestMapping("/testRedirect") 
public String testRedirect(){ 
    return "redirect:/testHello"; 
}

视图控制器

当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view

controller标签进行表示

例如想实现访问首页,但没有任何操作就可以不需要写一个control方法进行解析,直接使用配置spring的配置文件添加mvc:view-controller进行实现即可。

注:

当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需

要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:

<mvc:annotation-driven />

<!--path:设置处理的请求地址 view-name:设置请求地址所对应的视图名称 --> 
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>

RESTful

REST:Representational State Transfer,表现层资源状态转移。

RESTful是一种常见的REST应用,是遵循REST风格的web服务。Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

类似于java的重载,在springmvc中通过@PathVariable注解来获取变量的类型和值进行匹配, 还可以通过不同的请求方法来进行不同的操作。在Springmvc里可以通过@RequestMapping(value = "/hello",method = {RequestMethod.GET}这个注解实现方法重载。类似注解的变体还有@GetMapping ,@PostMapping,@PutMapping,@DeleteMapping,@PatchMapping。(ps: 所有的地址栏请求默认都会是 HTTP GET 类型的)。

使用传统方法进行请求等操作

 http://127.0.0.1/item/queryItem.action?id=1 查询,GET
 http://127.0.0.1/item/saveItem.action 新增,POST
 http://127.0.0.1/item/updateItem.action 更新,POST
 http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

改用RESTful风格后:

 http://127.0.0.1/item/1 查询,GET
 http://127.0.0.1/item 新增,POST
 http://127.0.0.1/item 更新,PUT
 http://127.0.0.1/item/1 删除,DELETE

HiddenHttpMethodFilter

由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?

SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们 POST 请求转换为 DELETE PUT 请求

HiddenHttpMethodFilter 处理put和delete请求的条件:

a>当前请求的请求方式必须为post

b>当前请求必须传输请求参数_method

满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数

method的值,因此请求参数method的值才是最终的请求方式

在web.xml中注册HiddenHttpMethodFilter

<filter> 
    <filter-name>HiddenHttpMethodFilter</filter-name> 
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> 
<filter-mapping> 
    <filter-name>HiddenHttpMethodFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping>

处理静态资源

<!--开启mvc的注解驱动--> 
<mvc:annotation-driven />
<!-- 配置默认的servlet处理静态资源,如果要使用handler必须先开启注解驱动 -->
<mvc:default-servlet-handler />

文件上传下载

上传

下载

拦截器

配置

SpringMVC中的拦截器用于拦截控制器方法的执行

SpringMVC中的拦截器需要实现HandlerInterceptor

SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:

<bean class="com.atguigu.interceptor.FirstInterceptor"></bean> 
<ref bean="firstInterceptor"></ref> 
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 --> 
<mvc:interceptors> 
    <mvc:interceptor> 
        <mvc:mapping path="/**"/> 
        <mvc:exclude-mapping path="/testRequestEntity"/> 
        <ref bean="firstInterceptor"></ref> 
    </mvc:interceptor> 
</mvc:interceptors>
<!--以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过 mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求 -->

拦截器的三个方法

SpringMVC中的拦截器有三个抽象方法:

preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返

回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法

postHandle:控制器方法执行之后执行postHandle()

afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()

拦截器的执行顺序

①若每个拦截器的preHandle()都返回true

此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:

preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行

②若某个拦截器的preHandle()返回了false

preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false

的拦截器之前的拦截器的afterCompletion()会执行

异常处理

SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和

SimpleMappingExceptionResolver

SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:

XML处理异常

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 
    <property name="exceptionMappings"> 
        <props> 
            <!--properties的键表示处理器方法执行过程中出现的异常 properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面 --> 
            <prop key="java.lang.ArithmeticException">error</prop> 
        </props> 
    </property> 
    <!--exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享 --> 
    <property name="exceptionAttribute" value="ex"></property> 
</bean>

注解处理异常

//@ControllerAdvice将当前类标识为异常处理的组件 
@ControllerAdvice public class ExceptionController { 
    //@ExceptionHandler用于设置所标识方法处理的异常 
    @ExceptionHandler(ArithmeticException.class) 
    //ex表示当前请求处理中出现的异常对象 
    public String handleArithmeticException(Exception ex, Model model){ 
        model.addAttribute("ex", ex); return "error"; 
    } 
}

SpringMVC执行流程

DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求

HandlerMapping:处理器映射器,不需要工程师开发,由框架提供作用:根据请求的url、method等信息查找Handler,即控制器方法

Handler:处理器,需要工程师开发

作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理

HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供

作用:通过HandlerAdapter对处理器(控制器方法)进行执行

ViewResolver:视图解析器,不需要工程师开发,由框架提供

作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、

RedirectView

View:视图

作用:将模型数据通过页面展示给用户

SSM整合

ContextLoaderListener

Spring提供了监听器ContextLoaderListener,实现ServletContextListener接口,可监听

ServletContext的状态,在web服务器的启动,读取Spring的配置文件,创建Spring的IOC容器。web

应用中必须在web.xml中配置

<listener> 
    <!--
		配置Spring的监听器,在服务器启动时加载Spring的配置文件
        Spring配置文件默认位置和名称:/WEB-INF/applicationContext.xml 
        可通过上下文参数自定义Spring配置文件的位置和名称
 	--> 
    <listener-class>org.springframework.web.context.ContextLoaderListener
    </listener-class> 
</listener> 
<!--自定义Spring配置文件的位置和名称--> 
<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>classpath:spring.xml</param-value> 
</context-param>

springMVC-config

<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描控制层组件-->
    <context:component-scan base-package="edu.mk.music.controller"></context:component-scan>
	<!-- 视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/> 
                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
    <!--首页 -->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
    <!--开启mvc的注解驱动-->
    <mvc:annotation-driven/>
    <!-- 配置默认的servlet处理静态资源,如果要使用handler必须先开启注解驱动 -->
    <mvc:default-servlet-handler/>
</beans>

spring-config配置

spring需要扫描除了Control层以外的所有包

<?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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 排除扫描Control -->
    <context:component-scan base-package="edu.mk.music">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 引入jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!-- 配置Druid数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 配置用于创建SqlSessionFactory的工厂bean -->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 设置MyBatis配置文件的路径(可以不设置) -->
        <property name="configLocation" value="classpath:Mybatis-Config.xml"></property>
        <!-- 设置数据源 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- 设置类型别名所对应的包 -->
        <property name="typeAliasesPackage" value="edu.mk.music.pojo"></property>
        <!--设置映射文件的路径 若映射文件所在路径和mapper接口所在路径一致,则不需要设置 -->
        <property name="mapperLocations" value="classpath:mappers/*.xml"> </property>
    </bean>
    <!--配置mapper接口的扫描配置 由mybatis-spring提供,可以将指定包下所有的mapper接口创建动态代理 并将这些动态代理作为IOC容器的bean管理 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="edu.mk.music.mapper"></property>
    </bean>
</beans>

log4j

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://jakarta.apache.org/log4j/ ">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

mybatis-cfg

<?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>
    <settings> 
        <!--将下划线映射为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <plugins> <!--配置分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins></configuration>