Spring

1、认识Spring

Spring全家桶:Spring SpringMVC SpringBoot SpringCloud SpringCloud-alibaba SpringSecurity SpringData ……

Spring框架,被称为胶水框架,本身没有发明出新的技术,使现有的java技术,如何更好用。

现有的web开发中遇到的问题:

1、耦合度高,依赖所创建的对象

在业务逻辑层中

DeptDao deptDao=new DeptDaoImpl();

在servlet中

DeptService deptService=new DeptServiceImpl();

2、java原生的API比较复杂、开发效率低 JDBC

Spring框架的特点:

1、提供了很多技术的解决方案---IoC AOP 事务管理等

2、没有发明新技术,使现有技术更好用---胶水框架

3、优秀的框架,使用了很多设计模式:单例模式、简单工厂设计模式、代理设计模式、观察者、模板、策略等


2、Spring的体系结构

Test: 

测试,用来测试程序

CoreContainer:

核心容器

Beans: 对象,也称为bean IoC

Core: 其他模块所依赖的核心模块

Context: 上下文,包括调度和远程抽象

SpEL:Spring提供的支持的表达式语言 提供了很多标签

Aop:

面向切面编程

Spring-Aop:基于代理的Aop的支持

Aspects:基于AspectsJ的切面

Instrument :JVM调用的一个用于监测目的代理

messaging: 对消息框架的支持

数据访问部分 :

Data Access

将Mybatis和Spring融合在一起

jdbc: spring-data jdbc-template 对JDBC封装 简化jdbc的操作

orm:整合第三方的ORM框架,mybatis hibernate等 简化mybaits等ORM框架的使用

Transactions: 整合了事务---声明式事务,简化事务的操作,无需编程式事务中,手动提交、回滚事务

web支持:

servlet:使用

web: web网页

MVC:整合MVC框架

GroupIdArtifactId说明
org.springframeworkspring-beansBeans 支持,包含 Groovy
org.springframeworkspring-aop基于代理的AOP支持
org.springframeworkspring-aspects基于AspectJ 的切面
org.springframeworkspring-context应用上下文运行时,包括调度和远程抽象
org.springframeworkspring-context-support支持将常见的第三方类库集成到 Spring 应用上下文
org.springframeworkspring-core其他模块所依赖的核心模块
org.springframeworkspring-expressionSpring 表达式语言,SpEL
org.springframeworkspring-instrumentJVM 引导的仪表(监测器)代理
org.springframeworkspring-instrument-tomcatTomcat 的仪表(监测器)代理
org.springframeworkspring-jdbc支持包括数据源设置和 JDBC 访问支持
org.springframeworkspring-jms支持包括发送/接收JMS消息的助手类
org.springframeworkspring-messaging对消息架构和协议的支持
org.springframeworkspring-orm对象/关系映射,包括对 JPA 和 Hibernate 的支持
org.springframeworkspring-oxm对象/XML 映射(Object/XML Mapping,OXM)
org.springframeworkspring-test单元测试和集成测试支持组件
org.springframeworkspring-tx事务基础组件,包括对 DAO 的支持及 JCA 的集成
org.springframeworkspring-webweb支持包,包括客户端及web远程调用
org.springframeworkspring-webmvcREST web 服务及 web 应用的 MVC 实现
org.springframeworkspring-webmvc-portlet用于 Portlet 环境的MVC实现
org.springframeworkspring-websocketWebSocket 和 SockJS 实现,包括对 STOMP 的支持
org.springframeworkspring-jclJakarta Commons Logging 日志系统


3、IoC( Inverse Of Controll:控制反转 )

将new对象交由Spring来创建而不是自己创建

Bean:咖啡豆,转义为 java对象

自己创建对象,例如在业务逻辑层,需要创建dao的对象,从而调用dao中的方法,依赖于创建对象的类

把对象的创建、管理交给spring来进行,降低了对类的依赖,这样耦合度低

自定义框架:

实现创建对象的功能,当程序员需要对象时,通过框架获取即可

1、新建maven项目

2、创建实体类

3、创建配置文件,配置要创建的对象信息---bean的定义信息

properties文件

zhangsan=com.qf.springpro2105.pojo.Student
dev=com.qf.springpro2105.pojo.Dept

4、编写工厂类,创建声明的对象

4.1:先读取bean的定义信息

4.2:根据读取到的bean的定义信息,通过反射方式创建对象

package com.qf.springpro2105.util;

import java.io.IOException;
import java.util.Properties;

public class MyBeanFactory {
    //创建Properties对象
    private Properties properties=new Properties();

    public MyBeanFactory() {
    }
    //参数就是配置文件的名字
    public MyBeanFactory(String configName) {

        try {
            //加载配置文件信息
            properties.load(MyBeanFactory.class.getResourceAsStream(configName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取对象
    public Object getBean(String beanName){
        //1、从配置文件中读取到要创建的对象的类名
        String className=properties.getProperty(beanName);

        if(className!=null){
            Class clazz=null;
            try {
                //2、通过反射创建对象
                clazz=Class.forName(className);
                return clazz.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }

        }
        return null;
    }
}

5、测试

package com.qf.springpro2105.test;

import com.qf.springpro2105.pojo.Dept;
import com.qf.springpro2105.pojo.Student;
import com.qf.springpro2105.util.MyBeanFactory;

public class TestMyBeanFactory {
    public static void main(String[] args) {
        //创建工厂类对象
        //     /beans.properties  中/的含义是项目的根目录
        MyBeanFactory beanFactory=new MyBeanFactory("/beans.properties");
        //获取对象
        Object object=beanFactory.getBean("zhangsan");
        if(object!=null){
            Student jiaBao=(Student)object;
            System.out.println(jiaBao);
        }

        Dept dev=(Dept)beanFactory.getBean("dev");
        System.out.println(dev);
    }
}

spring中bean的功能就相当于我们自定义的工厂

IoC内部框架:

为了降低耦合度,把对象的创建、管理的权利交给spring进行

1、搭建spring环境

1.1  添加依赖

<!-- Spring常用依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

1.2  编写spring的配置文件,名字任意,有意义即可

      spring-context.xml

【说明】spring支持bean的声明/定义方式有很多

1、xml方式

2、注解方式

3、properties方式等  ---   zhangsan = 类地址(包名)

<?xml version="1.0" encoding="UTF-8"?>
<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="jiaBao" class="com.qf.springpro2105.pojo.Student"></bean>
    <bean id="dev" class="com.qf.springpro2105.pojo.Dept"></bean>
</beans>

1.3  获取bean

System.out.println("----------------------------------------------");
System.out.println("spring获取bean");
ApplicationContext beanFactory2=new ClassPathXmlApplicationContext("spring-context.xml");
Student jiaBao2= (Student) beanFactory2.getBean("zhangsan");
System.out.println(jiaBao2);

2、给对象的属性赋值

DI: Dependency Injection 依赖注入,在创建对象的过程中,给属性赋值的过程

也就是说给属性赋值的过程叫注入

<?xml version="1.0" encoding="UTF-8"?>
<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="jiaBao" class="com.qf.springpro2105.pojo.Student">
        <!--
            每一个property就是class里指定类的一个属性
            其中name代表属性名,value是属性值
            默认情况下:调用getBean()时,才创建对象
            如果设置了property,则在创建对象后,调用setXXX()方法,给属性赋值
        -->
        <property name="stuName" value="张三"></property>
        <property name="stuNo" value="qf001"></property>
    </bean>
    <bean id="dev" class="com.qf.springpro2105.pojo.Dept">
        <property name="dName" value="软件研发2105部"></property>
        <property name="deptNo" value="1001"></property>
        <property name="loc" value="北京市海淀区"></property>
    </bean>
</beans>

bean的创建过程:---简化的过程

1、声明bean (xml 文件 注解 properties文件)

2、读取配置文件 (声明的方式不同,读取的方式也不同)

3、通过反射创建对象

4、通过setXXX()方法给属性赋值

DI:不同类型的属性值如何注入

//User类
public class User {
    //不同类型的属性如何注入值
    private Integer id;
    private String password;
    private String sex;
    private Date bornDate;
    private String[] hobbys;
    private Set<String> phones;
    private List<String> names;
    private Map<String,String> countries;
    private Properties files;
    private Student[] stus;
}

<!--set注入-->
<?xml version="1.0" encoding="UTF-8"?>
<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="jiaBao" class="com.qf.springpro2105.pojo.Student">
        <!--
            每一个property就是class里指定类的一个属性
            其中name代表属性名,value是属性值
            默认情况下:调用getBean()时,才创建对象
            如果设置了property,则在创建对象后,调用setXXX()方法,给属性赋值
        -->
        <property name="stuName" value="王佳保"></property>
        <property name="stuNo" value="qf001"></property>
    </bean>
    <bean id="yinZuEn" class="com.qf.springpro2105.pojo.Student">
    </bean>
    <bean id="dev" class="com.qf.springpro2105.pojo.Dept">
        <property name="dName" value="软件研发2105部"></property>
        <property name="deptNo" value="1001"></property>
        <property name="loc" value="设计城906"></property>
    </bean>

    <bean id="mingYuan" class="com.qf.springpro2105.pojo.Emp">
        <!--DI:给对象的属性赋值-->
        <property name="empNo" value="1001"></property>
        <property name="eName" value="江明源"></property>
        <!--dept属性是一个对象类型,如何注入?
        属性如果是对象类型,通过ref注入对象的属性值
        -->
        <property name="dept" ref="dev"></property>

    </bean>
    <!--给不同类型的属性注入值-->
    <bean id="haoTian" class="com.qf.springpro2105.pojo.User">
        <property name="id" value="1001"></property>
        <property name="password" value="123abc"></property>
        <property name="sex" value="mail"></property>
        <property name="bornDate" value="2000/04/08"></property><!--注意格式/-->
        <!--可以存多个值的类型:容器类型-->
        <property name="hobbys">
            <array>
                <value>Run</value>
                <value>Code</value>
                <value>sleep</value>
            </array>
        </property>
        <property name="phones">
            <set>
                <value>13888888888</value>
                <value>13888888886</value>
            </set>
        </property>
        <property name="names">
            <list>
                <value>tom</value>
                <value>harry</value>
                <value>jack</value>
            </list>
        </property>
        <property name="countries">
            <map>
                <entry key="CN" value="中国"></entry>
                <entry key="US" value="美国"></entry>
                <entry key="KR" value="韩国"></entry>
            </map>
        </property>
        <property name="files">
            <props>
                <prop key="first">第一个文档</prop>
                <prop key="second">第二个文档</prop>
                <prop key="third">第三个文档</prop>
            </props>
        </property>
        <property name="stus">
            <array>
                <!--内部bean-->
                <bean id="tao" class="com.qf.springpro2105.pojo.Student"></bean>
                <!--可以引入外部bean-->
                <ref bean="jiaBao"></ref>
                <ref bean="yinZuEn"></ref>
            </array>
        </property>
     </bean>

</beans>

注入方式

Spring给对象的属性注入值的方式有很多:

1、设值注入,通过setXXX()方法来给属性赋值

通过bean的property方式,使用的就是设值注入(如上)

2、构造注入,必须提供有参构造方法(如下)   

<!--构造注入-->
<bean id="zeYang" class="com.qf.springpro2105.pojo.Student">
    <constructor-arg name="stuNo" value="qf003"></constructor-arg>
    <constructor-arg name="stuName" value="王泽洋"></constructor-arg>
</bean>

3、自动注入,自动装配

      无需配置赋值的代码,根据名字或类型自动赋值

package com.qf.springpro2105.pojo;

public class Teacher {
    private String teaName;
    private Student stu;

    public String getTeaName() {
        return teaName;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "teaName='" + teaName + '\'' +
                ", stu=" + stu +
                '}';
    }

    public void setTeaName(String teaName) {
        this.teaName = teaName;
    }

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }
}
<bean id="stu" class="com.qf.springpro2105.pojo.Student">
    <property name="stuNo" value="qf007"></property>
    <property name="stuName" value="王冰"></property>
</bean>
<!--使用自动注入
 autowire="byName" 根据名字的自动注入,找bean的id值和Teacher类的属性名一致的bean进行赋值
 byType:要求和属性同类型的bean只能有一个
-->
<bean id="liu" class="com.qf.springpro2105.pojo.Teacher" autowire="byName">
    <property name="teaName" value="刘老师"></property>
</bean>

Bean的作用域

【说明】SpringIoC创建Bean 是使用工厂模式

默认情况下都是单例Bean

作用域共有5个:

1、单例singleton:此bean默认为单例模式

优势:节省内存,不管getBean()获取多少次,就创建一个对象

缺点:多线程情况下,产生线程安全问题(springMVC框架时讲解)

2、多例/原型prototype :每次通过beanfactory的getBean()方法都会创建一个新的对象

3、request: bean在同一次请求内有效

4、session: bean在同一次会话内有效

5、global session: 所有会话都有效

FactoryBean

在spring中bean分两种类型:

1、普通bean ,之前使用的bean 特点:能够通过反射方式直接创建出来

2、FactoryBean,工厂Bean/复杂Bean, 实现了FactoryBean接口的bean,不能通过反射直接创建出来,需要若干步骤才能被创建

例如:mybatis中的SqlSession对象,不能通过反射直接创建

 创建FactoryBean

package com.qf.springpro2105.pojo;

import org.springframework.beans.factory.FactoryBean;

import java.io.Serializable;

public class Book implements Serializable, FactoryBean {
    private String bookName;
    private float price;
    private String isbn;

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public Object getObject() throws Exception {
        //编码,实现多个步骤创建对象
        //创建SqlSessionBuilder
        //SqlSessionFactory
        //SqlSession
        //return SqlSession对象
        Publisher publisher=new Publisher();
        publisher.setBook(this);
        publisher.setPublisherName("人民教育出版社");
        return publisher;
    }

    public Class<?> getObjectType() {
        //得到对象的类型
        return Publisher.class;
    }

    public boolean isSingleton() {
        //是否是单例
        return false;
    }
}
package com.qf.springpro2105.pojo;

public class Publisher {
    private String PublisherName;
    private Book book;

    public String getPublisherName() {
        return PublisherName;
    }

    public void setPublisherName(String publisherName) {
        PublisherName = publisherName;
    }

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    //这样的话只需要给book赋值就可以了,调用当前方法,因为xml给book赋值了
    //直接得到book的各种属性名了
    public void say(){
        System.out.println("本出版社"+this.PublisherName+"出版了图书《"
                +this.book.getBookName()+"》价格是"+this.book.getPrice()
                +"ISBN条形码是"+this.book.getIsbn());
    }
}

BeanFactroy的特性

饿汉模式:占内存

当工厂创建的时候,配置文件中配置的bean就一并被创建

懒汉模式:创建bean工厂时,并不创建bean,当通过beanfactroy的getBean()获取bean的时候,才会创建bean   效率低

 

【面试题】如何设置不让工厂创建某个类的对象?

1、bean 标签中 lazy-init="true", 不统一创建,在通过getBean()获取时创建

2、bean 标签中 abstract="true", 不会被spring的工厂创建

【说明】通常都默认

Bean的生命周期

 

生命周期方法的执行顺序

1、构造方法 2、setXXX() 3、初始化方法 4、销毁方法

当初始化执行完毕后,对象才创建结束,可以使用

如何设置生命周期里的初始化方法和销毁方法呢?

有两种方式:

方式一:自定义方法名,通过bean标签的init-method方法指定初始化方法

通过bean标签的destroy-method方法指定销毁方法

方式二:实现spring里的初始化、销毁接口,自动执行实现的方法

package com.qf.springpro2105.pojo;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import java.io.Serializable;

public class Animal2 implements Serializable, InitializingBean, DisposableBean {
    public Animal2() {
        System.out.println("Animal2的构造方法");
    }
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("执行了Animal的setName方法");
        this.name = name;
    }

    public void afterPropertiesSet() throws Exception {
        //初始化方法,由于实现接口,会自动执行
        System.out.println("执行了Animal2类中的初始化方法");
    }

    public void destroy() throws Exception {
        //销毁时执行的方法
        System.out.println("执行了Animal类中的销毁方法");
    }
}
<bean id="wangCai2" class="com.qf.springpro2105.pojo.Animal2">
    <property name="name" value="旺财2"></property>
</bean>

生命周期的总结:

单例bean: 默认是饿汉模式的bean,随着工厂的创建而创建--->构造方法--->setXXX()--->初始化方法--->对象创建完毕--->随着工厂的销毁而销毁

多例bean:通过beanfactory的getBean()获取时而创建(懒汉模式的bean)-->构造方法--->setXXX()--->初始化方法--->对象创建完毕--->JVM的GC来回收、销毁

【说明】bean由工厂创建完毕后,会存入map集合中

代理设计模式

做一件事情,可以设置代理,让代理去做,简化编程。

案例:

静态代理:只能为某个接口或某个类做代理

接口

package com.qf.springpro2105.proxy;

import com.qf.springpro2105.pojo.Dept;

public interface DeptDao {
    int addDept(Dept dept);
}

接口的实现类

package com.qf.springpro2105.proxy;

import com.qf.springpro2105.pojo.Dept;

public class DeptDaoImpl implements DeptDao {
    public int addDept(Dept dept) {
        System.out.println("添加部门"+dept.getDeptNo()+"到数据库中");
        return 1;
    }
}

代理类

package com.qf.springpro2105.proxy;

import com.qf.springpro2105.pojo.Dept;
//代理类---实现哪个接口,就给谁做代理
//给固定类型做代理---静态代理
public class DeptProxy implements DeptDao {
    //创建实现类的对象
    private DeptDao deptDao=new DeptDaoImpl();
    public int addDept(Dept dept) {
        //设置公共的代码
        //通过代理去调用方法
        System.out.println("即将执行添加方法");
        int result=deptDao.addDept(dept);
        System.out.println("执行添加方法结束");
        return result;
    }
}

测试类:直接使用代理即可

package com.qf.springpro2105.proxy;

import com.qf.springpro2105.pojo.Dept;

import javax.crypto.spec.DESedeKeySpec;

public class Test {
    public static void main(String[] args) {
        //创建部门对象
        Dept dept=new Dept();
        dept.setDeptNo(9118);
        dept.setdName("研发部");
        dept.setLoc("设计城906");
        //创建代理
        DeptDao deptDao=new DeptProxy();
        int result=deptDao.addDept(dept);
        if(result==1){
            System.out.println("添加部门成功");
        }else{
            System.out.println("添加部门失败");
        }
    }
}

动态代理设计模式:可以给任意类型做代理,灵活 (jdk动态代理,cglib动态代理)

jdk动态代理

package com.qf.springpro2105.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//jdk动态代理
public class DyProxy<T> implements InvocationHandler {
    public DyProxy() {
    }

    public DyProxy(T proxy) {
        this.proxy = proxy;
    }

    //声明属性:接收所代理的对象
    private T proxy;

    public T getProxy() {
        return proxy;
    }

    public void setProxy(T proxy) {
        this.proxy = proxy;
    }
    //调用invoke方法,就是在执行/调用代理对象对象的方法
    //参数1:proxy  代理对象
    //参数2:执行的代理对象中的方法
    //参数3:执行代理对象方法所需要的参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //声明变量,接收执行代理对象方法后的返回值
        Object result=null;
        //执行代理对象方法前,编写业务代码
        System.out.println("动态代理开始执行……");

        try{
            result=method.invoke(this.proxy,args);

            //执行代理方法后,编写业务代码
            System.out.println("动态代理方法执行结束");
        }catch (Exception ex){
            System.out.println("执行代理对象的方法,发生异常,异常信息如下:"+ex.getMessage());
        }finally {
            System.out.println("执行了代理对象的finally方法");
        }

        return result;
    }
}

测试

package com.qf.springpro2105.proxy;

import com.qf.springpro2105.pojo.Dept;
import com.qf.springpro2105.pojo.Emp;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class TestDyProxy {
    public static void main(String[] args) {
        //使用jdk动态代理
        //不直接调用DeptDaoImpl   EmpDaoImpl中的方法
        //使用动态代理对象去调

        //1、创建被代理的对象
        DeptDao deptDao=new DeptDaoImpl();
        EmpDao empDao=new EmpDaoImpl();

        //2、设置代理
        InvocationHandler handler=new DyProxy<DeptDao>(deptDao);

        //3、绑定---创建动态代理对象的过程
        DeptDao proxy= (DeptDao) Proxy.newProxyInstance(DeptDao.class.getClassLoader(),
                new Class[]{DeptDao.class},handler);
        //执行代理方法
        Dept dept=new Dept();
        dept.setDeptNo(9099);
        proxy.addDept(dept);

        System.out.println("--------------------------------------------------------");
        //设置代理
        InvocationHandler handler1=new DyProxy<EmpDao>(empDao);

        //创建代理对象
        EmpDao proxy1=(EmpDao)Proxy.newProxyInstance(EmpDao.class.getClassLoader(),
                new Class[]{EmpDao.class},handler1);
        Emp emp=new Emp();
        emp.setEmpNo(1001);
        proxy1.addEmp(emp);
    }
}

【说明】JDK动态代理有要求:接口+接口的实现类

通过反射方式实现

【面试题】:反射在程序中的应用场景有哪些?

1、SpringIoC:用反射来创建bean

2、动态代理:代理对象的创建

3、自己:优化servlet

cglib动态代理

1、添加依赖

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>2.2.2</version>
</dependency>

2、创建代理类

package com.qf.springpro2105.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
//cglib动态代理
public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer=new Enhancer();
    //获取代理独享
    public Object getProxy(Class calzz){
        //设置代理类的父类,给谁做代理,谁就是父类
        enhancer.setSuperclass(calzz);
        enhancer.setCallback(this);
        //JVM动态生成所代理对象的子类(字节码文件)
        //动态生成的代理类,给谁做代理继承谁
        return enhancer.create();
    }
    //o:代理对象
    //objects:参数
    //methodProxy  方法的代理对象
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //执行代理对象方法之前的逻辑代码
        System.out.println("cglib动态代理开始执行……");
        Object obj=null;
        try{
            obj=methodProxy.invokeSuper(o,objects);
            System.out.println("cglib懂爱代理执行结束");
        }catch (Exception ex){
            System.out.println("cglib动态代理执行时发生异常,异常信息如下:"+ex.getMessage());
        }finally {
            System.out.println("执行了cglib动态代理的finally");
        }
        return obj;
    }
}

测试

package com.qf.springpro2105.cglib;

public class test {
    public static void main(String[] args) {
        CglibProxy proxy=new CglibProxy();
        //获取JVM生成的代理对象
        DoSomething doSomething=(DoSomething)proxy.getProxy(DoSomething.class);
        //doSomethis里存储的是DoSomething子类对象,子类对象就是DoSomething类的代理对象
        doSomething.doSomeThing();
    }
}

【说明】cglib代理不要求代理对象实现接口

cglib原理是通过动态生产代理类对象的子类作为代理对象的方式

【面试题】jdk动态代理和cglib动态代理的区别?

mybatis使用的是jdbc动态代理

spring里两种代理方式都使用,优先使用jdk动态代理,如果类没有实现接口,才是用cglib动态代理

如果不理解代理模式,将无法理解Aop

【总结】

动态代理:

jdk:

    类中:
    类名设置泛型T  实现InvocationHandler --- 重写invok方法(利用反射)
    添加属性 类型为T类型 --- 该属性代表的是动态代理的对象
    设置Constructor和setget方法
    重写invok方法(可在该方法中编写业务代码)
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
    try-catch中 写入 method.invok(this.proxy,args) --- 用Object类型参数来接收  
    return Object类型的参数
     测试:
    实现被代理的对象  --- newDaoImpl
    设置代理对象  --- InvocationHandler handler=new DyProxy<接口>(接口的变量名);
    绑定动态代理的对象 --- 接口类型 变量名1= (接口类型) Proxy.newProxyInstance(接        口.class.getClassLoader(), new Class[]{接口.class},handler);
    此时变量名1可调用该接口所存在的方法

        


cglib:
     类中:
    类名实现MethodInterceptor接口 --- 重写intercept方法
    new Enhancer对象
    创建代理独享的方法 --- 参数传Class 
    方法中实现 enhancer.setSuperclass(参数名); enhancer.setCallback(this);return enhancer.create(); 
    重写intercept方法(可在该方法中编写业务代码)
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {}
    try-catch中 写入 methodProxy).invokSuper(o,objects) --- 用Object类型参数来接收
    return obj
     测试:
    new 动态代理的类(上边的类)
    调用类中创建代理独享的方法 --- 参数传被动态代理类的对象类型
    就可以调用被动态代理中的方法
    
    
    【区别】 jdk本质是反射  
                  cglib本质是设置父类的方式将代理的类作为父类

                  jdk运用接口+接口的实现类的方式   调用的方法所在的类要实现接口
                  cglib无需创建接口可直接创建类并将其作为父类进行调用
                  
                  jdk实现InvocationHandler接口  重写invoke方法          该接口是jdk所自带的
                  cglib实现MethodInterceptor接口 重写intercept方法   该接口需导入cglib依赖

                  jdk创建T类型的属性  将其声明为代理对象
                  cglib创建代理独享的方法 创建Enhancer 将其setSuperclass  setCallback  create传进该方法中
 


4、Aop

AOP:Aspect Oriented Programming 面向切面编程

桌子:OOP 面向对象编程 桌面对象、桌腿对象、螺丝钉对象、抽屉对象等

航天飞机:比较复杂,用OOP设计程序的话,对象非常多,不好管理,辅助使用Aop来设计程序

问题:注册用户功能,只需要添加到数据库吗?显然不是,在执行核心功能的同时,还要执行辅助功能

核心功能:把用户添加到数据库中

辅助功能/通用功能:异常处理

事务处理

记录日志等

Aop的思想,让程序员只关注(编写)核心功能,辅助功能通过配置后,自动执行


                
5、Aop的核心思想

  • 连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。

  • 切入点(Pointcut):被Spring切入连接点。

  • 通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。

  • 目标对象(Target):代理的目标对象

  • 引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。

  • 织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。

  • 代理(Proxy):被AOP织入通知后,产生的结果类。

  • 切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。


6、搭建SpringAop开发环境

添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>

修改spring配置文件的头信息,增加了aop部分的xsd的限制

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       ">
</beans>

7、Aop的实现

编写核心方法

package com.qf.springpro2105.service.impl;

import com.qf.springpro2105.pojo.Dept;
import com.qf.springpro2105.service.DeptService;

public class DeptImpl implements DeptService {
    public boolean addDept(Dept dept) {
        System.out.println("添加部门"+dept.getDeptNo()+"成功!");
        return true;
    }
}

编写通用方法

记录日志:记录谁、在什么时间、在哪台电脑、做了什么事

异常日志:记录什么时间、发生什么异常,为了调试使用

添加log4j的依赖

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

添加log4j的配置文件

名字必须为:log4j.properties

# Global logging configuration
log4j.rootLogger=DEBUG, stdout,logfile
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=qf.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

编写增强方法

注意:增强方法,可以写到一个类中,也可以写到多个类中

记录日志的增强方法

package com.qf.springpro2105.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;

import java.util.Arrays;

public class OperatorAdvice {
    //声明Log4j对象,用于记录日志
    private final Logger logger=Logger.getLogger(OperatorAdvice.class);
    //编写通用方法
    //前置增强方法:在核心方法之前执行---记录日志
    //获取执行核心方法的信息
    //JoinPoint连接点对象:可以获取PointCut的信息
    //增强的执行顺序并不是按照自上而下的顺序,而是将切入点放在将被定义的增强当中
    public void beforeOper(JoinPoint joinPoint){
        //记录日志
        logger.debug("即将执行"+joinPoint.getTarget()+"类里的"+
                joinPoint.getSignature()+"方法,方法传参如下:"+
                Arrays.toString(joinPoint.getArgs()));
    }

    //后置增强方法:在执行核心方法之后执行---记录日志
    public void afterOper(JoinPoint joinPoint,Object result){
        //记录日志
        logger.debug("执行"+joinPoint.getTarget()+"类中的方法"+
                joinPoint.getSignature()+"结束,方法的返回值如下:"+result);
    }
}

异常处理增强、最终增强

package com.qf.springpro2105.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;

public class ExceptionAdvice {
    //在catch  finally   记录日志
    Logger logger=Logger.getLogger(ExceptionAdvice.class);
    //异常处理增强:发生异常时所执行的通用方法
    //参数1:spring传递过来的连接点对象,封装了切入点核心方法的信息
    //参数2:spring在执行核心方法发生异常时,传递过来的异常对象
    public void catchException(JoinPoint joinPoint,RuntimeException ex){
        System.out.println("发生异常,异常信息如下:"+ex.getMessage());
        //error,写到控制台或日志文件,红色文字打印
        logger.error(joinPoint.getSignature()+"类中的方法"+
                joinPoint.getSignature()+"发生异常,异常信息如下:"+ex.getMessage());
    }

    //最终增强
    public void finallyException(JoinPoint joinPoint){
        logger.debug(joinPoint.getSignature()+"类中的方法"+
                joinPoint.getSignature()+"执行finally结束");
    }
}

xml文件方式配置Aop

切入点(核心方法)的配置有两种:

1、精确配置,某个特定的方法

2、通用表达式配置,代表一类方法

<!--定义核心方法bean信息-->
<bean id="deptService" class="com.qf.springpro2105.service.impl.DeptServiceImpl"></bean>
<!--定义增强方法的bean-->
<bean id="operAdvice" class="com.qf.springpro2105.aop.OperatorAdvice"></bean>
<bean id="exceptionAdvicee" class="com.qf.springpro2105.aop.ExceptionAdvice"></bean>

<!--Aop配置-->
<aop:config>
    <!--告知spring核心方法是谁,也称为切入点
        切入点的配置有两种:
        方式一:配置固定方法(特定某个方法)
        方式二:表达式配置,配置一类方法(多个方法)
        .. 参数类型任意
    -->
    <!--方式一:配置固定方法(特定某个方法)-->
    <!--<aop:pointcut id="addDeptPointCut" expression="execution(public boolean addDept(Dept))"/>-->

    <!--方式二:表达式配置,配置一类方法(多个方法)-->
    <aop:pointcut id="aopPointCut" expression="execution(public * com.qf.springpro2105.service..*.*(..))"/>
    <aop:pointcut id="aopPointCut2" expression="execution(public String com.qf.springpro2105.service..*.*(..))"/>
    <aop:pointcut id="aopPointCut3" expression="execution(public String com.qf.springpro2105.service..*.*(String,int))"/>
    <!--配置切面-->
    <aop:aspect ref="operAdvice">
        <!--前置增强-->
        <aop:before method="beforeOper" pointcut-ref="aopPointCut"></aop:before>
        <!--后置增强-->
        <aop:after-returning method="afterOper" pointcut-ref="aopPointCut" returning="result"></aop:after-returning>
    </aop:aspect>

    <aop:aspect ref="exceptionAdvicee">
        <!--异常处理增强-->
        <aop:after-throwing method="catchException" pointcut-ref="aopPointCut" throwing="ex"></aop:after-throwing>
        <!--最终增强-->
        <aop:after method="finallyException" pointcut-ref="aopPointCut"></aop:after>
    </aop:aspect>
</aop:config>

环绕增强

声明环绕增强

package com.qf.springpro2105.aop;

//环绕增强

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;

import java.util.Arrays;

public class ArroundAdvice {
    //创建日志对象
    Logger logger=Logger.getLogger(ArroundAdvice.class);

    public Object doArround(ProceedingJoinPoint joinPoint){
        //声明存放返回值的变量
        Object result=null;
        //相当于前置增强的代码
        logger.debug("环绕增强---执行了"+joinPoint.getTarget()+"里的方法"+
                joinPoint.getSignature()+"方法的参数如下:"+
                Arrays.toString(joinPoint.getArgs()));

        try{
            //执行切入点方法---核心方法
            result=joinPoint.proceed();

            //相当于后置增强
            logger.debug("环绕增强---执行"+joinPoint.getSignature()+"方法结束,"+
                    "方法的返回值如下:"+result);

        }catch (Throwable throwable) {
            //throwable.printStackTrace();
            //相当于异常处理增强
            logger.error("环绕增强---执行"+joinPoint.getSignature()+"发生异常,异常信息如下:"+
                    throwable.getMessage());
        } finally {
            //相当于最终增强
            logger.debug("环绕增强---执行"+joinPoint.getSignature()+"finally结束");
        }
        return result;
    }
}

配置环绕增强

<!--定义环绕增强的bean-->
<bean id="arround" class="com.qf.springpro2105.aop.ArroundAdvice"></bean>
<aop:aspect ref="arround">
    <aop:around method="doArround" pointcut-ref="aopPointCut"></aop:around>
</aop:aspect>

【说明】切入点如果使用了接口,则spring使用jdk动态代理(效率较高)

如果没有使用接口,则spring使用cglib动态代理

注解方式配置aop

//注解方式配置aop
@Component//创建类的对象存储在ioc容器中
@Aspect//声明切面,相当于配置文件里的<aop:config>
public class Advice {
    private final Logger logger=Logger.getLogger(Advice.class);
    //声明切入点的pointCut,execution设置的是对哪些方法进行拦截增强
    @Pointcut("execution(* com.qf.java2105.service.impl..*.*(..))")
    public void method(){};


    //配置前置增强
    @Before("method()")
    public void doBefore(JoinPoint joinPoint){
        logger.debug("正在执行"+joinPoint.getTarget()+"类里的"+
            joinPoint.getSignature()+"方法");
    }


    //配置后置增强
    @AfterReturning(value = "method()",returning = "result")
    public void doAfter(JoinPoint joinPoint,Object result){
        logger.debug("正在执行"+joinPoint.getTarget()+"类里的"+
            joinPoint.getSignature()+"方法,返回值为"+result);
    }



    //异常处理增强
    @AfterThrowing(value = "method()",throwing = "ex")
    public void doThrowing(JoinPoint joinPoint,Exception ex){
        logger.error("正在执行"+joinPoint.getTarget()+"类里的"+
            joinPoint.getSignature()+"方法,异常信息为"+ex.getMessage());
    }


    
    @After("method()")
    public void doFinally(JoinPoint joinPoint){
        logger.debug("正在执行"+joinPoint.getTarget()+"类里的"+
            joinPoint.getSignature()+"方法");
    }


}


8、后处理器

是bean的生命周期里的一个阶段

1、构造方法 2、setXXX() 3、后处理器前置阶段 4、初始化方法 5、 后处理器后置阶段 创建完成 6、销毁

package com.qf.springpro2105.aop;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;

//后处理器:对象已经被创建
//执行分两个阶段:
//1、在初始化方法之前执行,称为后处理器的前置阶段
//2、在初始化方法之和执行,称为后处理器的后置阶段
public class MyBenPostProcessor implements BeanPostProcessor {
    //后处理器的前置阶段  所执行的方法
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后处理器 在init之前执行,"+bean.getClass());
        return bean;
    }

    //后处理器的后置阶段 所执行的方法
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后处理器 在init之后执行,"+bean.getClass());
        return bean;
    }
}

<!--配置后处理器,将对bean工厂中的所有bean的生命周期进行干预-->
<bean class="com.qf.springpro2105.aop.MyBenPostProcessor"></bean>


9、spring整合mybatis

为什么要整合?

使mybatis更易用。

【面试题】spring都整合了mybais的哪些组件?

1、mybatis工具类里的对象 SqlSessionFactoryBuilder SqlSessionFactory SqlSession

使用Spring-IoC功能实现的

2、整合了事务---spring自动提交、回滚事务,声明式事务

使用Spring-Aop功能实现的

3、Spring自动获取Dao接口的实现类对象(代理类对象),放在容器中

两个版本的整合:

版本一:配置文件方式    xml

版本 二:注解方式(主要使用)

添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>

    <!-- Spring整合mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>

    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>

    <!-- mysql驱动 依赖 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.25</version>
        <scope>runtime</scope>
    </dependency>

    <!-- 连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
    </dependency>
</dependencies>
<build>
    <!-- 更改maven编译规则 -->
    <resources>
        <resource>
            <!-- 资源目录 -->
            <directory>src/main/java</directory>
            <includes>
                <include>*.xml</include><!-- 默认(新添加自定义则失效) -->
                <include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

编写配置文件

数据库配置文件

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3

 xml整合

引入外部文件

    <!--
        引入外部文件
        引入外部文件用 context:property-placeholder
        引入文件需要用文件的类路径  + properties文件名
    -->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>

配置连接池信息

    <!--
        配置连接池信息
        id:自己起的名
        class : 引入连接池信息
        init-method : 初始化方法  init : 连接池里的初始化方法 - 方法自己就初始化了
        destroy-method :销毁方法  close :连接池里的销毁方法 - 结束之后自己就销毁了
    -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    init-method="init" destroy-method="close">
        <!--
            property是Druid连接池自带的名
            value是db.properties里的名
        -->
            <property name="driverClassName" value="${jdbc.driverClass}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>


        <!--配置连接池的其他信息-->

        <!--
            配置超时时间  设定时间范围之外就不等待了
            maxWait : 配置等待时间的
            value : 是以毫秒为单位的
            maxActive  最大活跃数量
            minIdle    最小空闲 数量
            initialSize  初始化数量
        -->
            <property name="maxWait" value="5000"></property>
            <property name="initialSize" value="${jdbc.init}"></property>
            <property name="maxActive" value="${jdbc.maxActive}"></property>
            <property name="minIdle" value="${jdbc.minIdle}"></property>
    </bean>

创建sqlSessionFactory

 <!--
       说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
       然后创建 SqlSessionFactory对象

       在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
       所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可

       "org.mybatis.spring.SqlSessionFactoryBean"  : Spring和Mybatis整合依赖里的
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--
            注入数据源属性
            name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
            相当于就是把原先的mybatis连接池替换成Druid连接池
        -->
        <property name="dataSource" ref="druidDataSource"></property>
        <!--
            配置映射文件的位置
            typeAliasesPackage : 配置别名
            value写实体类的别名位置
            mapperLocations : 映射文件位置  是一个集合类型
        -->
        <property name="typeAliasesPackage" value="com.qf.springandmybatis.pojo"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:com/qf/springandmybatis/dao/*.xml</value>
            </list>
        </property>
    </bean>

将接口类对象放在bean工厂中

    <!--
        获取接口的代理类对象,放在bean工厂中
        配置使用sqlSessionFactory里的sqlSession来执行
        org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
    -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--
            dao接口的位置
            basePackage : 基本包  value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
        -->
        <property name="basePackage" value="com.qf.springandmybatis.dao"></property>

        <!--
           配置:到哪里获取sqlSession
           value中放入的是sqlSessionFactory
           因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
        -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

配置ServiceImpl层的对象

<!--
        配置deptServiceImpl对象
        autowire: 自动装配 byType类型  byName名字
        id给测试的getBean用
    -->
    <bean id="deptService" class="com.qf.springandmybatis.service.impl.DeptServiceImpl" >
        <!--
            到IoC的bean工厂中(集合)获取对象,注入给deptDao属性
            ref="deptDao"中的deptDao,是mybatis创建的接口的代理类对象,接口名的字母小写
            name是Impl中set的名
            ref是dao层的类名
            这么写是将dao层的接口与service的Impl绑定在一起
        -->
        <property name="deptDao" ref="deptDao"></property>
    </bean>



xml整体

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qf</groupId>
    <artifactId>springandmybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <packaging>war</packaging>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!-- Spring整合mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- mysql驱动 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
    </dependencies>
    <build>
        <!-- 更改maven编译规则 -->
        <resources>
            <resource>
                <!-- 资源目录 -->
                <directory>src/main/java</directory>
                <includes>
                    <include>*.xml</include><!-- 默认(新添加自定义则失效) -->
                    <include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

</project>

spring-context.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       ">
    <!--
        引入外部文件
        引入外部文件用 context:property-placeholder
        引入文件需要用文件的类路径
    -->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <!--
        配置连接池信息
        id:自己起的名
        class : 引入连接池信息
        init-method : 初始化方法  init : 连接池里的初始化方法 - 方法自己就初始化了
        destroy-method :销毁方法  close :连接池里的销毁方法 - 结束之后自己就销毁了
    -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    init-method="init" destroy-method="close">
        <!--
            property是Druid连接池自带的名
            value是db.properties里的名
        -->
            <property name="driverClassName" value="${jdbc.driverClass}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>


        <!--配置连接池的其他信息-->

        <!--
            配置超时时间  设定时间范围之外就不等待了
            maxWait : 配置等待时间的
            value : 是以毫秒为单位的
            maxActive  最大活跃数量
            minIdle    最小空闲 数量
            initialSize  初始化数量
        -->
            <property name="maxWait" value="5000"></property>
            <property name="initialSize" value="${jdbc.init}"></property>
            <property name="maxActive" value="${jdbc.maxActive}"></property>
            <property name="minIdle" value="${jdbc.minIdle}"></property>
    </bean>

    <!--
       说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
       然后创建 SqlSessionFactory对象

       在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
       所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可

       "org.mybatis.spring.SqlSessionFactoryBean"  : Spring和Mybatis整合依赖里的
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--
            注入数据源属性
            name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
            相当于就是把原先的mybatis连接池替换成Druid连接池
        -->
        <property name="dataSource" ref="druidDataSource"></property>
        <!--
            配置映射文件的位置
            typeAliasesPackage : 配置别名
            value写实体类的别名位置
            mapperLocations : 映射文件位置  是一个集合类型
        -->
        <property name="typeAliasesPackage" value="com.qf.springandmybatis.pojo"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:com/qf/springandmybatis/dao/*.xml</value>
            </list>
        </property>
    </bean>

    <!--
        获取接口的代理类对象,放在bean工厂中
        配置使用sqlSessionFactory里的sqlSession来执行
        org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
    -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--
            dao接口的位置
            basePackage : 基本包  value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
        -->
        <property name="basePackage" value="com.qf.springandmybatis.dao"></property>

        <!--
           配置:到哪里获取sqlSession
           value中放入的是sqlSessionFactory
           因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
        -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

    <!--
        配置deptServiceImpl对象
        autowire: 自动装配 byType类型  byName名字
        id给测试的getBean用
    -->
    <bean id="deptService" class="com.qf.springandmybatis.service.impl.DeptServiceImpl" >
        <!--
            到IoC的bean工厂中(集合)获取对象,注入给deptDao属性
            ref="deptDao"中的deptDao,是mybatis创建的接口的代理类对象,接口名的字母小写
            name是Impl中set的名
            ref是dao层的类名
            这么写是将dao层的接口与service的Impl绑定在一起
        -->
        <property name="deptDao" ref="deptDao"></property>
    </bean>
</beans>

数据库properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3

pojo

package com.qf.springandmybatis.pojo;

import java.io.Serializable;

public class Dept implements Serializable {
    private int deptNo;
    private String dName;
    private String loc;

    public Dept() {
    }

    public Dept(int deptNo, String dName, String loc) {
        this.deptNo = deptNo;
        this.dName = dName;
        this.loc = loc;
    }

    public int getDeptNo() {
        return deptNo;
    }

    public void setDeptNo(int deptNo) {
        this.deptNo = deptNo;
    }

    public String getdName() {
        return dName;
    }

    public void setdName(String dName) {
        this.dName = dName;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptNo=" + deptNo +
                ", dName='" + dName + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}

dao---接口

package com.qf.springandmybatis.dao;

import com.qf.springandmybatis.pojo.Dept;

public interface DeptDao {
    int addDept(Dept dept);
}

dao---Mapper

<?xml version="1.0" encoding="UTF8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.springandmybatis.dao.DeptDao">
     <insert id="addDept" parameterType="Dept">
         insert into dept(deptno,dname,loc)
         values
         (#{deptNo},#{dName},#{loc})
     </insert>
</mapper>

Service---接口

package com.qf.springandmybatis.service;

import com.qf.springandmybatis.pojo.Dept;

public interface DeptService {
    boolean addDept(Dept dept);
}

Service---Impl

package com.qf.springandmybatis.service.impl;

import com.qf.springandmybatis.dao.DeptDao;
import com.qf.springandmybatis.pojo.Dept;
import com.qf.springandmybatis.service.DeptService;

public class DeptServiceImpl implements DeptService {
    //需要调用dao  创建DeptDao对象
    //不需要自己new,springIoC中已经存储了mybatis创建的该接口类型的代理类对象
    //需要注入即可
    private DeptDao deptDao;
    //供设置注入时调用
    public void setDeptDao(DeptDao deptDao){
        this.deptDao=deptDao;
    }
    @Override
    public boolean addDept(Dept dept) {
        int result = deptDao.addDept(dept);
        return result==1;
    }

}

Test

package com.qf.springandmybatis.test;

import com.qf.springandmybatis.pojo.Dept;
import com.qf.springandmybatis.service.DeptService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        DeptService deptService = (DeptService) context.getBean("deptService");
        Dept dept = new Dept();
        dept.setDeptNo(9020);
        dept.setdName("9020");
        dept.setLoc("9020");
        boolean result = deptService.addDept(dept);
        if(result){
            System.out.println("添加部门成功");
        }else{
            System.out.println("添加部门失败");
        }
    }
}



注解整合

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tr="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">


    <!--
    引入外部文件
    引入外部文件用 context:property-placeholder
    引入文件需要用文件的类路径
-->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <!--
        配置连接池信息
        id:自己起的名
        class : 引入连接池信息
        init-method : 初始化方法  init : 连接池里的初始化方法 - 方法自己就初始化了
        destroy-method :销毁方法  close :连接池里的销毁方法 - 结束之后自己就销毁了
    -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--
            property是Druid连接池自带的名
            value是db.properties里的名
        -->
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>


        <!--配置连接池的其他信息-->

        <!--
            配置超时时间  设定时间范围之外就不等待了
            maxWait : 配置等待时间的
            value : 是以毫秒为单位的
            maxActive  最大活跃数量
            minIdle    最小空闲 数量
            initialSize  初始化数量
        -->
        <property name="maxWait" value="5000"></property>
        <property name="initialSize" value="${jdbc.init}"></property>
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <property name="minIdle" value="${jdbc.minIdle}"></property>
    </bean>

    <!--
       说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
       然后创建 SqlSessionFactory对象

       在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
       所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可

       "org.mybatis.spring.SqlSessionFactoryBean"  : Spring和Mybatis整合依赖里的
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--
            注入数据源属性
            name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
            相当于就是把原先的mybatis连接池替换成Druid连接池
        -->
        <property name="dataSource" ref="druidDataSource"></property>
        <!--
            配置映射文件的位置
            typeAliasesPackage : 配置别名
            value写实体类的别名位置
            mapperLocations : 映射文件位置  是一个集合类型
        -->
        <property name="typeAliasesPackage" value="com.qf.springandmybatispro2.pojo"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:com/qf/springandmybatispro2/dao/*.xml</value>
            </list>
        </property>
    </bean>

    <!--
        1)获取接口的代理类对象,放在bean工厂中
        2)配置使用sqlSessionFactory里的sqlSession来执行
        org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
    -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--
            dao接口的位置
            basePackage : 基本包  value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
        -->
        <property name="basePackage" value="com.qf.springandmybatispro2.dao"></property>

        <!--
           配置:到哪里获取sqlSession
           value中放入的是sqlSessionFactory
           因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
        -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>


    <!--
        如果使用注解方式,通过配置告知spring,扫描指定包下的类,按类中的注解执行
        通过base-package设置父包即可  告知所有注解的父包就可以了
        spring会扫描父包及子包下的所有类
    -->
    <context:component-scan base-package="com.qf.springandmybatispro2"></context:component-scan>

    <!--开启Aop的注解方式-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


    <!--创建事务管理器-->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--
            指定数据源使用该事务
            告诉德鲁伊使用创建的tx管理器了
            每当使用一个需要用到连接池的就要将他替换成Druid
        -->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--
        配置事务管理器来控制事务
        利用的就是aop的方式生成一个额外的处理
    -->
    <!--配置事务管理器来控制事务-->
    <tx:advice id="txManager" transaction-manager="tx">
        <tx:attributes>
            <!--
              <tx:method name=""/> name里写的方法名就是需要添加事物的方法名
              add*代表着以add开头的方法
              <tx:method name="add*"/> 意思指 add开头的所有方法都添加事务
            -->
            <tx:method name="add*"/>
            <tx:method name="update*"/>
            <tx:method name="delete"></tx:method>
            <!--所有方法都使用事务-->
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>

</beans>



注解 整体

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qf</groupId>
    <artifactId>springandmybatispro2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!-- Spring整合mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- mysql驱动 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>
    <build>
        <!-- 更改maven编译规则 -->
        <resources>
            <resource>
                <!-- 资源目录 -->
                <directory>src/main/java</directory>
                <includes>
                    <include>*.xml</include><!-- 默认(新添加自定义则失效) -->
                    <include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

</project>

spring-context.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tr="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">


    <!--
    引入外部文件
    引入外部文件用 context:property-placeholder
    引入文件需要用文件的类路径
-->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <!--
        配置连接池信息
        id:自己起的名
        class : 引入连接池信息
        init-method : 初始化方法  init : 连接池里的初始化方法 - 方法自己就初始化了
        destroy-method :销毁方法  close :连接池里的销毁方法 - 结束之后自己就销毁了
    -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--
            property是Druid连接池自带的名
            value是db.properties里的名
        -->
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>


        <!--配置连接池的其他信息-->

        <!--
            配置超时时间  设定时间范围之外就不等待了
            maxWait : 配置等待时间的
            value : 是以毫秒为单位的
            maxActive  最大活跃数量
            minIdle    最小空闲 数量
            initialSize  初始化数量
        -->
        <property name="maxWait" value="5000"></property>
        <property name="initialSize" value="${jdbc.init}"></property>
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <property name="minIdle" value="${jdbc.minIdle}"></property>
    </bean>

    <!--
       说明 在mybatis里先创建 SqlSessionFactoryBuilder对象
       然后创建 SqlSessionFactory对象

       在Spring中,SqlSessionFactoryBean封装了一个类SqlSessionFactoryBuilder和SqlSessionFactory
       所以spring整合mybatis后,只要创建SqlSessionFactoryBean即可

       "org.mybatis.spring.SqlSessionFactoryBean"  : Spring和Mybatis整合依赖里的
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--
            注入数据源属性
            name : dataSource 就是 SqlSessionFactoryBean 里的dataSource
            相当于就是把原先的mybatis连接池替换成Druid连接池
        -->
        <property name="dataSource" ref="druidDataSource"></property>
        <!--
            配置映射文件的位置
            typeAliasesPackage : 配置别名
            value写实体类的别名位置
            mapperLocations : 映射文件位置  是一个集合类型
        -->
        <property name="typeAliasesPackage" value="com.qf.springandmybatispro2.pojo"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:com/qf/springandmybatispro2/dao/*.xml</value>
            </list>
        </property>
    </bean>

    <!--
        1)获取接口的代理类对象,放在bean工厂中
        2)配置使用sqlSessionFactory里的sqlSession来执行
        org.mybatis.spring.mapper.MapperScannerConfigurer 可以完成上述两个功能
    -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--
            dao接口的位置
            basePackage : 基本包  value只要写上路径自己就去扫描去了并且把各个类放在bean工厂里
        -->
        <property name="basePackage" value="com.qf.springandmybatispro2.dao"></property>

        <!--
           配置:到哪里获取sqlSession
           value中放入的是sqlSessionFactory
           因为在上边已经创建了,而且这里边有SqlSessionFactoryBuilder和SqlSessionFactory
        -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>


    <!--
        如果使用注解方式,通过配置告知spring,扫描指定包下的类,按类中的注解执行
        通过base-package设置父包即可  告知所有注解的父包就可以了
        spring会扫描父包及子包下的所有类
    -->
    <context:component-scan base-package="com.qf.springandmybatispro2"></context:component-scan>

    <!--开启Aop的注解方式-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


    <!--创建事务管理器-->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--
            指定数据源使用该事务
            告诉德鲁伊使用创建的tx管理器了
            每当使用一个需要用到连接池的就要将他替换成Druid
        -->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--
        配置事务管理器来控制事务
        利用的就是aop的方式生成一个额外的处理
    -->
    <!--配置事务管理器来控制事务-->
    <tx:advice id="txManager" transaction-manager="tx">
        <tx:attributes>
            <!--
              <tx:method name=""/> name里写的方法名就是需要添加事物的方法名
              add*代表着以add开头的方法
              <tx:method name="add*"/> 意思指 add开头的所有方法都添加事务
            -->
            <tx:method name="add*"/>
            <tx:method name="update*"/>
            <tx:method name="delete"></tx:method>
            <!--所有方法都使用事务-->
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>

</beans>

db.properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2105pro?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3

pojo

package com.qf.springandmybatispro2.pojo;

import java.io.Serializable;

public class Dept implements Serializable {
    private int deptNo;
    private String dName;
    private String loc;

    public Dept() {
    }

    public Dept(int deptNo, String dName, String loc) {
        this.deptNo = deptNo;
        this.dName = dName;
        this.loc = loc;
    }

    public int getDeptNo() {
        return deptNo;
    }

    public void setDeptNo(int deptNo) {
        this.deptNo = deptNo;
    }

    public String getdName() {
        return dName;
    }

    public void setdName(String dName) {
        this.dName = dName;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptNo=" + deptNo +
                ", dName='" + dName + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}

aop---Advice

package com.qf.springandmybatispro2.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//注解方式配置aop
@Aspect  //身份:是一个切面 相当于配置文件里的<aop:config>
@Component  //创建类的对象,然后把对象放在IoC容器中  相当于<bean id="" class="">
public class Advice {
    //声明切入点PointCut
    @Pointcut("execution(public * com.qf.springandmybatispro2.service..*.*(..))")
    public void method(){} //方法的声明,其中方法的名字就是切入点的通用表达式所代表的方法

    //配置前置增强
    @Before("method()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature() + "方法将被执行");
    }

    //配置后置增强
    @AfterReturning(value = "method()",returning = "result")
    public void doAfter(JoinPoint joinPoint,Object result){
        System.out.println(joinPoint.getSignature() + "方法执行结束,返回值是:" +result);
    }

    //异常处理增强
    @AfterThrowing(value = "method()",throwing = "ex")
    public void doThrowing(JoinPoint joinPoint,Throwable ex){
        System.out.println("执行"+joinPoint.getSignature()+"发生异常,异常信息如下:" + ex.getMessage());
    }

    //最终增强
    @After("method()")
    public void doFinally(JoinPoint joinPoint){
        System.out.println("执行" + joinPoint.getSignature()+"的finally");
    }

//    //了解
//    //环绕增强
//    @Around("method()")
//    public void around(ProceedingJoinPoint joinPoint,Throwable e){
//        Object result = null;
//        //前置增强
//        System.out.println("执行了前置增强");
//    }
}

dao---接口

package com.qf.springandmybatispro2.dao;


import com.qf.springandmybatispro2.pojo.Dept;

public interface DeptDao {
    int addDept(Dept dept);
}

dao---Mapper

<?xml version="1.0" encoding="UTF8" standalone="no"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.springandmybatispro2.dao.DeptDao">
     <insert id="addDept" parameterType="Dept">
         insert into dept(deptno,dname,loc)
         values
         (#{deptNo},#{dName},#{loc})
     </insert>
</mapper>

service---接口

package com.qf.springandmybatispro2.service;

import com.qf.springandmybatispro2.pojo.Dept;

public interface DeptService {
    boolean addDept(Dept dept);
}

service---Impl

package com.qf.springandmybatispro2.service.Impl;

import com.qf.springandmybatispro2.dao.DeptDao;
import com.qf.springandmybatispro2.pojo.Dept;
import com.qf.springandmybatispro2.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("deptService")  //创建对象,放于bean容器中  相当于<bean id="" class=""> 对象的默认名字是接口名字的首字母小写
//@Scope("prototype")  设置bean的作用域 默认为单例  prototype为多例
@Transactional  //类中的每一个方法都是独立的一个事务  这里也可以全局控制
public class DeptServiceImpl implements DeptService {
    @Autowired  //按照类型自动装配,到bean容器中,找该类型的bean 然后赋值 如果找不到或找到多个就报异常
    private DeptDao deptDao;  //直接注入,无需封装setXXX()

    //在每一个方法的上方 指定当前方法如何使用事务
    //NEVER 从不使用事务
    //SUPPORTS 用事务就用没事务我就不用  因为在类上边已经写了Transactional 所以现在每个都是独立的 不整体加事务了
    //         就可以是使用编程式事务,自己编写以及提交 如果额外自己写了 那么就使用事务 没写就不用
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public boolean addDept(Dept dept) {
        int result = deptDao.addDept(dept);
        return result==1;
    }
}

Test

package com.qf.springandmybatispro2.test;

import com.qf.springandmybatispro2.pojo.Dept;
import com.qf.springandmybatispro2.service.DeptService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        //getBean中的名字是Service中的@Service("deptService")这个名字   如果不写这个名字,那么对象的默认名字是接口名字的首字母小写
        //例如接口名字为DeptService  那么生成的名字为deptService
        DeptService deptService = (DeptService) context.getBean("deptService");
        Dept dept = new Dept();
        dept.setDeptNo(6531);
        dept.setdName("6528");
        dept.setLoc("6528");
        boolean result = deptService.addDept(dept);
        if(result){
            System.out.println("部门添加成功");
        }else{
            System.out.println("部门添加失败");
        }
    }
}

spring测试

额外增加依赖(上边案例的pom中已添加)

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

spring-context.xml中的映射文件进行更改

Test文件夹下建包进行测试

package com.qf.springandmybatispro.dao;

import com.qf.springandmybatispro2.dao.DeptDao;
import com.qf.springandmybatispro2.pojo.Dept;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class) //运行器,Spring运行环境 用来运行Test 就可以获取bean工厂中的bean了
@ContextConfiguration("classpath:spring-context.xml") //读取配置文件里的内容
public class TestDept {
    @Autowired
    private DeptDao deptDao;

    @Test //无返回值就可以
    public void testAddDept(){
        Dept dept = new Dept();
        dept.setDeptNo(9000);
        dept.setdName("9000");
        dept.setLoc("9000");

        int result = deptDao.addDept(dept);
        if(result == 1){
            System.out.println("测试成功");
        }else{
            System.out.println("测试失败");
        }
    }
}



10、Spring的事务处理机制

Spring把mybatis中的事务进行了整合,利用Aop的思想进行整合,所以自动提交、回滚事务

声明式事务:使用Aop思想实现,简化事务的控制

在配置文件中创建事务管理器,控制事务

<!--创建事务管理器-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--指定数据源使用该事务-->
    <property name="dataSource" ref="druidDataSource"></property>
</bean>

【注意】事务管理器里的dataSource一定要和SqlSessionFactoryBean里是同一个dataSource

【说明】spring整合事务后,所有的方法都使用了事务,都会自动提交、回滚事务

事务管理器的作用:哪些方法使用事务,哪些方法不用事务

事务管理器的配置有两种方式:配置方式、注解方式

配置方式 了解---用的少

<!--配置事务管理器来控制事务-->
<tx:advice id="txManager" transaction-manager="tx">
    <tx:attributes>
        <tx:method name="add*"/>
        <tx:method name="update*"></tx:method>
        <tx:method name="delete*"></tx:method>
        <!--所有方法都使用事务-->
        <tx:method name="*"></tx:method>
    </tx:attributes>
</tx:advice>

注解方式使用事务管理器

【说明】1、事务用在业务罗层

2、事务使用Aop实现---代理对象

3、根据异常情况决定是提交事务还是回滚事务,不要在使用事务的业务逻辑代码里进行异常处理

事务的隔离级别

数据库是并发的,多个进程(线程)同时访问

例如:有两个事务同时去操作数据库,其中一个事务是修改id是1的这条数据,另一个事务是读取id是1的这个数据

执行情况:一个事务开启后,执行了update语句,但是没有提交,也没有回滚

这时另外一个事务来读取数据,如果读取了一个事务中未提交的数据,如果这个事务回滚了,那么当前事务读取到的就是脏数据,称为脏读。

事务的隔离级别:如何在并发环境中,如何控制多个事务之间的操作,由于数据库的锁机制造成的

写、写 互斥

读、读 不互斥

读、写 隔离级别来控制

通过4个值来控制事务的隔离级别:

名称描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

read-uncommited:读未提交,允许一个事务读取另一个事务中未提交的数据,造成脏读

read-commited:读取已提交,一个事务只能读取另一个事务中已经提交的数据,如果没有提交,则不允许另一个事务读取数据

repeatable-read:可重复读,同时存在多个读事务,多个写事务,读取已提交的事务的数据。

事务 多次读取,由于有多个写事务,第一次读取的数据(A事务提交的数据),和第二次读取的数据(B事务提交的数据),造成两次读取会不一致,造成幻读

serialized-read:序列化读 不允许读写事务之间同时进行,要串行,效率最低

由低到高:read-uncommited < read-commited < repeatable-read < serialized-read

【说明】级别越低,效率越高,越不安全

事务的传播行为/机制

传播行为/传播机制:每个业务逻辑层的方法都是一个事务,如果一个方法调用了另一个方法,这里两个事务如何共存的问题就称为事务的传播行为。

isolation:指定事务的隔离级别

propagation:指定事务的传播行为

传播行为有7个值

REQUIRED:必须有事务,当前方法必须执行事务里,如果没有事务就创建一个事务

如果已经存在事务则加入到该事务中

SUPPORTS: 可有可无,如果当前方法有事务,则使用当前事务,如果没有事务,就以非事务方式运行

MANDATORY:强制使用事务,使用当前的事务,如果没有事务,就抛异常

REQUIRES_NEW: 必须使用新的事务,如果当前方法已经有事务则挂起,创建新的事务

NOT_SUPPORTED: 以非事务方式运行,如果当前方法有事务,则挂起

NEVER: 以非事务方式运行,如果当前方法有事务,则抛出异常

NESTED: 如果当前方法存在事务,则事务进行嵌套,如果没有事务就新建事务

使用场景:主从表操作

业务逻辑层:方法1---插入订单主表,主键回调

方法2---插入订单从表

方法1 调用 方法2:REQUIRED

事务的其他属性

readOnly 设置为true: 只允许其他的读事务并行运行

默认为false: 允许其他的增删改查事务都并行运行

timeout 当前事务所操作的数据被其他事务占用,等待的时间

-1 由数据库决定

可以自行设置等待时间,以秒为单位

rollbackFor 回滚属性

如果事务中抛出 RuntimeException ,则事务自动回滚

如果事务中抛出 CheckException, 不自动回滚,则默认提交事务

处理方案,将CheckException转换成RuntimeException 往上抛 设置为Exception

【注意】不要在业务逻辑层捕获异常,如果捕获了,事务则接受不到异常,无法回滚

注解开发

定义Bean的注解:使用注解 相当于使用bean标签

  • @Service 业务类专用

  • @Repository dao实现类专用

  • @Controller web层专用

  • @Component 通用

  • @Scope 用户控制bean的创建模式

注入值的注解:

@Autowired //按类型自动装配,到bean容器中,找该类型的bean然后赋值,如果找不到,或找到多个就报异常
private DeptDao deptDao; //直接注入,无需封装setXXX()

@Resource //按名称自动装配,到bean工厂中(集合)找名字是deptDao2的bean,赋值给下面的属性
private DeptDao deptDao2;

@Resource(name = "deptDao4") //到bean工厂中找名字是deptDao4的对象,赋值给下面的属性
private DeptDao getDeptDao4;

@Autowired
@Qualifier("dd")  //当@Autowired按类型在工厂中找到多个同类型的Bean时,则使用名字为dd的Bean来注入给该属性
private DeptDao deptDao3;

@Value("设计城")//给8大基本类型+String 进行注入
private String defaultLoc;

事务的注解

可以用在类的上方,对类中的方法进行统一的事务配置

也可以用在某个方法的商法,对该方法进行单独配置

@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.NESTED,readOnly = true,timeout = -1,
    rollbackFor = Exception.class)

【注意】如果事务使用了注解的方式进行了配置,需要在spring的配置文件中开启事务

<!--开始事务的注解方式,并且制定事务管理器-->
<tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>

Aop的注解

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {
    // 定义切入点
    @Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")
    public void pc(){}
    
    @Before("pc()") // 前置通知
    public void mybefore(JoinPoint a) {
        System.out.println("target:"+a.getTarget());
        System.out.println("args:"+a.getArgs());
        System.out.println("method's name:"+a.getSignature().getName());
        System.out.println("before~~~~");
    }

    @AfterReturning(value="pc()",returning="ret") // 后置通知
    public void myAfterReturning(JoinPoint a,Object ret){
        System.out.println("after~~~~:"+ret);
    }
    
    @Around("pc()") // 环绕通知
    public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
        System.out.println("interceptor1~~~~");
        Object ret = p.proceed();
        System.out.println("interceptor2~~~~");
        return ret;
    }
    
    @AfterThrowing(value="pc()",throwing="ex") // 异常通知
    public void myThrows(JoinPoint jp,Exception ex){
        System.out.println("throws");
        System.out.println("===="+ex.getMessage());
    }
}

在配置文件中开启注解的方式

<!--开启Aop的注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

xml头文件

<?xml version="1.0" encoding="UTF-8" ?>
<!--suppress SpringFacetInspection -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
</beans>



SqlSessionFactoryBean的主要作用便是用来创建SqlSessionFactory,在它的构建过程中会解析MyBatis所需要的配置文件,将所有配置都封装到Configuration类中,最后返回SqlSessionFactory实例。(Spring中的sqlSessionFactoryBean中包含了SqlsessionFactoryBuilder方法和SqlsessionFactory方法)

sqlSessionFactory是mybatis中的一个重要的对象,通俗讲它是用来不断创建sqlSession对象的,每一个sqlsession对象对应一个mapper

sqlSession用来操作数据库的,通过SqlSession可以实现增删改查

当一个类没有被spring管理的时候,想在这个类中调用spring管理的bean以及其中的方法

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public final class SpringBeanTool implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static Object getBean(Class clazz) {
        return getApplicationContext().getBean(clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringBeanTool.applicationContext == null) {
            SpringBeanTool.applicationContext = applicationContext;
        }
    }
}
public class QuestionnaireData {

    public static boolean questionnaireDataState(String smId){
        HomePointsFaceBiz homePointsFaceBiz = (HomePointsFaceBiz) SpringBeanTool.getBean(HomePointsFaceBiz.class);
        HomePointsFace homePointsFace = new HomePointsFace();
        homePointsFace.setSmId(smId);
        HomePointsFace homePointsFaceDate = homePointsFaceBiz.selectOne(homePointsFace);
        if (Objects.isNull(homePointsFaceDate.getInvestigateType())) {
            return true;
        }
        throw new BizException("抛出异常");
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值