Spring 的学习笔记

Spring框架的核心思想

IOC:

  • 通俗定义技术原来手动通过new关键字创建对象的权力交给Spring,通过在配置文件配置bean标签形式创建对象 或者 交给Spring由Spring创建对象过程;

  • 概述:IOC控制反转,就是讲原来手动你通过new关键字创建对象的权力交给spring,有spring工厂创建对象过程,当然spring不仅创建对象还要在创建对象的同时通过DI的方式维护组件与组件的调用关系;
    DI:

  • 为组件中成员变量完成赋值过程,这个过程称之为 依赖注入

  • 在spring的配置文件中对应的组件标签内使用Property标签去完成属性的赋值操作;

DI基本语法

DAO组件:

public class DeptDaoImpl implements DeptDao{
    @Override
    public void save(String name) {
        System.out.println("1.DeptDaoImpl——> "+name);
    }
}

Service组件:

public class DeptServiceImpl implements DeptService{
    //需要组件DAO对象,依赖DAO组件
    private DeptDao deptDao;
    public void setDeptDao(DeptDao deptDao) {
        this.deptDao = deptDao;
    }
    @Override
    public void input(int age) {
        System.out.println("2.DeptServiceImpl——> "+age);
        deptDao.save("Eroc");
}

通过spring管理组件

<!--    通过spring组件
        bean: 用来管理组件对象的创建
        class: 用来指定管理罪案对象的全限定名 包·类
        id:  用来指定spring框架创建的当前组件对象在spring(容器|工厂)唯一标识
         -->
    <bean class="init.UserDaoImpl" id="a1"></bean>

启动工厂,获取对象进行测试:

//        启动工厂
        ApplicationContext context = new ClassPathXmlApplicationContext("init/spring.xml");
//        获取工厂创建好的对象,参数:获取工厂中指定对应的唯一标识
        UserDao userDao = (UserDao) context.getBean("a1");
        System.out.println(userDao);
        userDao.save("xiaoming");

spring工厂的相关特性

**注意:工厂默认在管理对象都是单例模式,无论在工厂中获取多少次,都是的同一对象

  1. 默认spring在管理组件对象是单例创建 singleton

  2. 如何修改工厂创建组件对象为多例

  3. spring工厂创对象的原理

    原理:反射+构造方法

    //        工厂原理
            TagDao tt = (TagDao) Class.forName("scop.TagDaoImpl").newInstance();
            System.out.println(tt);
    
  4. spring工厂管理组件生命周期

    1. 组件对象什么时候创建 2. 组件对象什么时候销毁

      单例对象:启动工厂中所有单例的对象随之创建 销毁工厂中所有单例对象随之销毁

      多例对象:每次工厂中使用时创建 工厂不负责多例对象的销毁

      <!--
          生命周期方法:
                   init-method=”“在对象初始化调用方法
                   destroy-method=“”在对象销毁时调用方法
               -->
          <bean class="adi.StudentServiceImpl" id="studentService" init-method="" destroy-method=""/>
      

Spring中注入方式

  1. SET注入 使用成员变量SET方式进行赋值
  2. 构造注入 使用构造方法形式进行属性的赋值

1. spring中SET方式的注入语法

  1. SET注入: 使用类中属性的SET方法为属性完成赋值的过程

  2. SET注入相关语法:

    <!--        SET注入相关语法,八种基本类型+String类型+Date类型注入  使用Value属性进行赋值-->
            <property name="name" value="asd"/>
            <property name="age" value="23"/>
            <property name="price" value="12.2"/>
            <property name="sex" value="true"/>
    <!--        注意:在spring技术栈中日期格式默认为yyyy/mm/dd HH:mm:ss-->
            <property name="bir" value="2021/12/12 23:54:23"/>
    <!--        注入普通数组类型-->
            <property name="arry">
                <array>
                    <value>a</value>
                    <value>b</value>
                    <value>c</value>
                </array>
            </property>
    <!--        注入引用对象数组类型ref/注入普通类型value-->
        <bean class="di.DeptDaoImpl" id="dept"/>
        <bean class="di.DeptServiceImpl" id="deptService">
            <property name="deptDaos">
                <array>
                    <ref bean="dept"></ref>
                    <ref bean="dept"></ref>
                    <ref bean="dept"></ref>
                </array>
            </property>
        </bean>
    <!--        注入引用对象集合类型ref/注入普通类型value-->
            <property name="habby">
                <list>
                    <ref bean="dept"/>
                    <ref bean="dept"/>
                </list>
            </property>
    <!--        注入map-->
            <property name="deptDaoMap">
                <map>
                    <entry key="Eric" value-ref="dept"/>
                    <entry key="xiaoming" value-ref="dept"/>
                </map>
            </property>
    <!--        注入Properties-->
            <property name="properties">
                <props>
                    <prop key="driver">收到货克鲁赛德</prop>
                    <prop key="url">http:www.baidu.com</prop>
                    <prop key="root">root</prop>
                </props>
            </property>
    
    public class DeptServiceImpl implements DeptService{
         private DeptDao deptDao;
        private String name;
        private Date dir;
        private DeptDao[] deptDaos;
        private List<DeptDao> habby;
        private Map<String,DeptDao> deptDaoMap;
        private Properties properties;
        /*省略中间的Set方法*/
        @Override
        public void input(int age) {
            System.out.println("2.DeptServiceImpl——> "+age);
            deptDao.save("Eroc");
            System.out.println("name:"+name+"Date:"+dir);
            System.out.println("--------数组遍历---------");
            for (Object ob:deptDaos
                 ) {
                System.out.println(ob);
            }
            System.out.println("--------集合遍历---------");
            habby.forEach(habby-> System.out.println("habby:"+habby));//lambada表达式
            System.out.println("--------Map遍历---------");
            deptDaoMap.forEach((key,value)-> System.out.println("key:"+key+" value:"+value));
            System.out.println("--------Properties遍历---------");
            properties.forEach((key,value)-> System.out.println("key:"+key+" value: "+value));
        }
    }
    

2.构造注入方式(不灵活)

使用类中构造方法为类中成员变量赋值过程,称之为构造注入

构造注入语法:

  • 需要哪个组件熟悉将谁声明为成员变量,比如提供公开构造方法
  • 在配置文件中对应的组价标签内部使用标签进行注入
<!--
    1.SET方法注入时, 使用Property标签
    2.构造方法注入时, 使用constructor-arg标签
    <constructor-arg index="给参数索引,从0开始"  name="构造参数名" value="参数赋值"/>
    -->
    <bean class="cdi.EmpDaoImpl" id="empDao">
        <constructor-arg index="0" name="name"  value="lixin"/>
    </bean>
    public EmpDaoImpl(String name) {
        this.name = name;
    }

自动注入

在spring工厂配置文件中通过制定自动注入方式,开始组件属性的自动赋值

**注意:

  • 底层使用原理是SET方式注入
  • 自动注入需要在对应组件标签开启才能使用
  • 只能用于引用类型的注入/对象类型/组件类型的注入

自动注入语法

  1. 需要谁将谁声明为成员变量,并提供SET方法
  2. 咋对应组件标签中加入autowired属性并指定自动注入方式即可完成注入

Spring中的 AOP

AOP: 面向切面的编程

java: (proxy)代理

  1. 代理对象:

    1. 保证原始业务逻辑,在原始业务逻辑功能不变的同时还可以完成一些附加的操作
    2. 依赖原始业务逻辑对象 ——>Target Object:目标对象 / 原始业务逻辑对象 / 被代理对象称之为目标对象
  2. 开发一个代理对象

    1. 代理对象和业务逻辑对象实现相同的接口
    2. 代理对象中依赖原始业务逻辑对象

**重点 *动态代理与静态代理

  1. 静态代理:为每一个业务层通过手动开发一个代理对象的过程称之为静态代理对象(不推荐)

  2. 动态代理:29.32段底层源码讲解

    1.     public static void main(String[] args) {
      //    动态代理对象:指的是程序运行过程中动态通过代码的方式为指定的类生成动态代理对象
      
              UserServiceImpl userService = new UserServiceImpl();//给指定的类生成动态代理对象
              System.out.println(userService.getClass());
      
      //        参数一:classLoader 类加载器
              ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
              
      //        参数二:Class[]目标对象的接口的类型的数组;
              Class[] classes = {UserServiceDao.class};
              
      //        参数三:InvocationHandler接口类型 invoke方法,用来书写额外功能 附加操作
      
      //        返回值:创建好的动态代理对象
              UserServiceDao userServiceDynamicProxy = (UserServiceDao) Proxy.newProxyInstance(classLoader, classes,
                      new InvocationHandler() {
                          @Override
      //            通过动态代理对象调用自己里面代理方法时会优先指定invoketionHandler类中invoke
      //            参数1:当前创建好的代理对象  参数2:当前代理对象执行的方法对象  参数3:当前代理对象执行方法的参数
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              System.out.println("当前执行的方法:"+method.getName());
                              System.out.println("当前执行方法的参数:"+args[0]);
                              try {
                                  System.out.println("开启事务");
      //                            调用目标类中业务方法通过反射机制 调用目标类中当前方法
                                  Object invoke = method.invoke(new UserServiceImpl(), args);
                                  System.out.println("提交事务");
                                  return invoke;
                              } catch (Exception e) {
                                  e.printStackTrace();
                                  System.out.println("回滚事务");
                              }
                              return null;
                          }
                      });
              System.out.println(userServiceDynamicProxy.getClass());
              String result = userServiceDynamicProxy.save("Eric");//通过动态代理对象调用代理中方法
              System.out.println(result);
          }
      
    2. public class UserServiceImpl implements UserServiceDao {
      
          @Override
          public String save(String name) {
              System.out.println("处理业务逻辑,调用save DAO");
              return name;
          }
      
          @Override
          public String delete(int id) {
              System.out.println("处理业务逻辑,调用delete DAO");
              return null;
          }
      
          @Override
          public void update(int id) {
              System.out.println("处理业务逻辑,调用update DAO");
          }
      
          @Override
          public String findAll(String name) {
              System.out.println("处理业务逻辑,调用findAll DAO");
              return null;
          }
      
          @Override
          public String findone(String name) {
              System.out.println("处理业务逻辑,调用findone DAO");
              return null;
          }
      
      }
      
    3. public interface UserServiceDao {
          String save(String name);
          String delete(int id);
          void update(int id);
          String findAll(String name);
          String findone(String name);
      }
      

spring中AOP编程

AOP:Aspect(切面) oriented(面向) programing 面向切面的编程

  • 底层原理:java代理设计模式——>动态代理

  • 优点:在原始业务逻辑功能不变的同时还可以完成一些附加的操作,将业务中核心 操作放在目标对象中执行,实现了附加操作和核心操作的解耦;

    • try {
             System.out.println("开启事务");
             Object invoke = method.invoke(new UserServiceImpl(), args);
             System.out.println("提交事务");
             return invoke;
      }catch (Exception e) {
             e.printStackTrace();
             System.out.println("回滚事务");
      }
      

通知(Advice):除了目标方法以外的操作都称之为通知(事务通知,日志通知,性能通知)

image-20210819120649148

切入点(poincut):指定开发好的通知应用于项目中那些组件那些方法,一般通知多用于业务层;

切面(Aspect):通知+切入点

重要:AOP切面编程:1.开发通知类(附加功能)2.配置切面点 3.组装切面(Aspect)

Spring AOP编程的步骤

  1. 引入aop编程相关依赖

    spring-aop , spring-expression , spring-aspect

  2. 为项目开发附加功能——通知(每一个通知,对应spring框架中的接口)

    1. 环绕通知 MethodIntercept

    2. 前置通知 MethodBeforeAdvice

    3. 后置通知 AfterReturningAdvice

    4. 异常通知 ThrowsAdvice

      //自定义记录业务方法名称 前置通知(前置通知:目标方法执行之前先执行的附加操作)
      public class MyBeforeAdvice implements MethodBeforeAdvice {
          @Override
          public void before(Method method, Object[] objects, Object o) throws Throwable {
              System.out.println("当前执行方法:"+method.getName());
              System.out.println("当前执行方法的参数:"+objects[0]);
              System.out.println("目标对象:"+o);
          }
      }
      
  3. 配置切面(spring.xml)

    a. 注册通知类

    <!--    注册通知-->
        <bean class="aop.MyBeforeAdvice" id="myBeforeAdvice"/>
    

    b.组装切面 aspect = advice+pointcut

    <aop:config>
        <!--        配置切入点 id:切入点在工厂中的唯一标识;
                         expression:用来指定切入项目中那些组件那些方法,
                                    execution(返回值 包.类名.方法(参数..))-->
    	<aop:pointcut id="pc" expression="execution(* aop.*ServiceImpl.*(..))"></aop:pointcut>
        <!--        配置切面 advice-ref:工厂中通知的id;pointcut-ref:工厂切入点唯一标识-->
    	<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc"/>
    </aop:config>
    
  4. 创建service组件

    public class EmpServceImpl implements EmpService{
        @Override
        public void save(String name) {
            System.out.println("save Dao处理业务逻辑:"+name);
        }
    
        @Override
        public String find(String name) {
            System.out.println("find Dao处理业务逻辑:"+name);
            return name;
        }
    }
    
  5. 测试

        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("aop/spring.xml");
            EmpService empServce = (EmpService) context.getBean("empService");
            empServce.find("Eric");
            System.out.println(empServce.getClass());
        }
    

标准AOP开发结构-环绕通知

  1. 引入aop编程相关依赖

    spring-aop , spring-expression , spring-aspect

  2. 开发业务组件

    1). DeptDao

    2).DeptDaoImpl

    3).DeptService

    4).DeptServiceImpl

  3. 开发环绕通知

    性能通知:计算业务层目标方法的执行时间

    //自定义环绕通知用来记录目标方法的执行时长
    public class MethodInvokeTimeAdvice implements MethodInterceptor {
    //    参数1:invocation 获取当前执行方法、获取挡圈执行方法的参数、获取目标对象
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            /*System.out.println("进入环绕通知");
            System.out.println("当前执行方法:"+methodInvocation.getMethod().getName());
            System.out.println("当前执行方法参数:"+methodInvocation.getArguments());
            System.out.println("获取当前对象:"+methodInvocation.getThis());*/
            try {
                long start = System.currentTimeMillis();
    //        放行目标方法
                Object procced = methodInvocation.proceed();//继续处理
                long end = System.currentTimeMillis();
                System.out.println("执行时间:"+(end-start));
                return procced;
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("出现异常时业务处理!");
            }
            return null;
        }
    }
    
  4. 配置spring.xml

    a).管理业务

    b).配置切面

    ​ 1). 注册通知组件

    ​ 2). 配置切面

    <!--    管理DAO-->
        <bean class="com.eric.dao.impl.DeptDaoImpl" id="deptDao"/>
        
    <!--    管理Service组件-->
        <bean class="com.eric.service.impl.DeptServiceImpl" id="deptService">
            <property name="deptDao" ref="deptDao"/>
        </bean>
        
    <!--    注册通知类-->
        <bean class="com.eric.advices.MethodInvokeTimeAdvice" id="invokeTimeAdvice"/>
        
    <!--    配置切面-->
        <aop:config>
    <!--        配置切入点-->
            <aop:pointcut id="pc" expression="execution(* com.eric.service.impl.*ServiceImpl.*(..))"/>
    <!--        组装切面-->
            <aop:advisor advice-ref="invokeTimeAdvice" pointcut-ref="pc"/>
        </aop:config>
    

Spring(pointcut)切入点表达式:

  • 作用:主要是用来决定项目中那些组件那些方法需要加入通知
    • expression=“切入点表达式”
      • 1.execution 切入点表达式 ——>方法级别的切入点表达式 控制粒度:方法级别 效率低
        • 完整语法:
          • 1.execution(访问权限修饰符 返回值 包名.类名.方法(参数类型))
          • 2.默认execution(返回值 包名.类名.方法(参数类型))
          • 符号解释:* 任意多个字符 ..任意多个
      • 2.within 切入点表达式——>类级别的切入点表达式 控制粒度:类级别 效率高 推荐
        • expression="within()"
        • 完整语法::
            1. within(包名.类名)
              1).within(com.eric.service.*ServiceImpl)

spring创建复杂对象

通过工厂创建简单对象

  • 简单对象:可以直接通过new关键字创建的对象,统一成为简单对象
    • 工厂创建时:

通过工厂创建复杂对象

  • 复杂对象:不能直接通过new关键字进行创建对象,例如:接口类型、抽象类类型

    • 工厂创建复杂对象:

      类(对象名FactoryBean) implement FactoryBean<Calendar | Connection | 复杂对象>

      public class MysqlFactoryBean implements FactoryBean<Connection> {
          //     创建复杂对象
          @Override
          public Connection getObject() throws Exception {
              Class.forName("com.mysql.jdbc.Driver");
              return DriverManager.getConnection("jdbc:mysql://localhost:3306/spbdemo","root","root");
          }
          //    指定复杂对象的类型
          @Override
          public Class<?> getObjectType() {
              return Connection.class;
          }
      
          @Override
          public boolean isSingleton() {
              return true;//    指定创建对象模式  true:单例;false:多例
          }
      }
      
  • 通过工厂配置创建复杂对象

    <!--    通过factorybean创建复杂对象-->
    <bean class="com.eric.factorybean.MysqlFactoryBean" id="conn"/>
    
  • 通过工厂获取

        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("com/eric/spring.xml");
            Connection mysqlFactoryBean = (Connection) context.getBean("conn");
            Connection conn = (Connection) context.getBean("conn");
            System.out.println(mysqlFactoryBean);
        }
    

Spring整合mybatis (mybatis是用来管理service,dao,entity)

思路分析(SM整合):

  • 引入相关依赖:spring mybatis mysql mybatis-Spring ....

  • 如何整合:

    Spring 项目管理框架 主要是用来负责项目中组件对象的创建,使用,销毁

    Mybatis 持久层框架 主要是用来简化原始jdbc技术对数据库访问操作

    整合思路:通过Spring框架接管Mybatis框架中核心对象的创建

  • Mybatis框架中核心对象:

    SqlSessionFatory(Mybatis核心对象)

    读取Mybatis-config.xml(数据源配置 mapper文件配置)

    ![image-20210823110018466](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210823110018466.png

  • SM整合spring框架+mybatis框架

    整合思路:通过Spring框架接管Mybatis中核心的SqlSessionFactory对象的创建;

    • Spring如何管理SQLSessionFactory对象的创建

    • 如何创建:

      is = Resources.getResourcesAsStream("mybatis-config.xml");
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
      
    • //举例
      SqlSessionFactoryBean implement FactoryBean<SqlSessionFactory>{
          SqlSessionFactory getObject(){
              is = Resources.getResourcesAsStream("mybatis-config.xml");
              sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
          }
          Class getclass(){return SqlSessionFactory.class};
          boolean isSingleton(){return true};
      }
      
      
    • 工厂管理SQLSessionFactory

      <bean class="xxx.sqlSessionFactoryBean" id="sqlSessionFactory"/>
      
    • 工厂获取

      //简写
      SqlSessionFactory sf = context.getBean("sqlSessionFactory");
      
  • Mybatis官方

    Mybatis-spring jar 封装 SqlSessionFactory对象的创建 SqlSessionFactoryBean

    注意:mybatis官方提供sqlSessionFactoryBean 不能在使用mybatis-config.xml

  • 创建数据源对象 druid

    • 引入依赖

          <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
          </dependency>
      
    • 创建数据源对象

      <!--    创建数据源对象 druid-->
          <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
              <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql://localhost:3306/spbdemo"/>
              <property name="username" value="root"/>
              <property name="password" value="root"/>
          </bean>
      <!--    创建SqlSessionFactory-->
          <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
      <!--        依赖数据源对象-->
              <property name="dataSource" ref="dataSource"/>
      <!--        依赖mapper文件注册-->
              <property name="mapperLocations">
                  <array>
                      <value>com/eric/UserDaoMepper.xml</value>
                  </array>
              </property>
          </bean>
      

SM整合至DAO层开发步骤:

  1. 引入相关依赖 spring mybatis mysql mybatis-spring druid

  2. 建表

  3. 实体类

  4. DAO接口

      public interface UserDao {
          List<Users> findAll();
      }
    
  5. Mapper配置文件

      <!--mapper.xml-->
      <mapper namespace="com.eric.dao.UserDao">
          <select id="findAll" resultType="com.eric.entity.Users">
              select id,name,age,birthday from user
          </select>
      </mapper>
    
  6. 编写spring.xml整合mybatis

    1. 创建DataSource,注入driverClassName,url,username,password;

        <!--    创建数据源-->
            <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:localhost:3306/spbdemo"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </bean>
      
    2. 创建SqlSessionFactory 使用SQLSessionFactoryBean 类 ,注入dataSource数据源 ,mapperLocations配置文件位置

             <!--    创建sqlSessionFactory-->
            <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
                <!--        依赖数据源-->
                <property name="dataSource" ref="dataSource"/>
                <!--        mapper文件注册-->
                <property name="mapperLocations">
                    <array>
                        <value>classpath:com/eric/userDaoMapper.xml</value>
                    </array>
                </property>
            </bean>
      
    3. 创建DAO 提供MapperFactoryBean类 ,注入sqlSessionFactory ,DAO接口类型(包.接口名)

         <!--    创建DAO组件类-->
             <bean class="org.mybatis.spring.mapper.MapperFactoryBean" id="userDao">
         <!--        注入SqlSessionFactory-->
                 <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
         <!--        注入接口类型 注入接口的全限定名(包.接口名)-->
                 <property name="mapperInterface" value="com.eric.dao.UserDao"/>
             </bean>
      
  7. 启动工厂获取

              ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
              UserDao userDao = (UserDao) context.getBean("userDao");
              userDao.findAll().forEach(users -> System.out.println(users));
    

SM 整合 之 DAO 和 Service 部分开发, 加入事务控制

  1. 引入依赖

  2. 建表

  3. 实体类

  4. DAO接口

  5. mapper配置文件

  6. 编写spring.xml

    a. 创建数据源 DataSource 注入driverClassName url username password

    <!--    创建DataSource-->
        <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/spbdemo"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
    

    b. 创建SqlSessionFactory 注入dataSource 注入mapperLocations 注入typeA...Pakcage别名设置;

    <!--    创建sqlSessionFactory-->
        <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
            <!--        依赖数据源-->
            <property name="dataSource" ref="dataSource"/>
            <!--       mapper文件注册-->
            <property name="mapperLocations">
                <array>
                    <value>classpath:com/eric/mapper/userDaoMapper.xml</value>
                    <value>classpath:com/eric/mapper/studentGoupMapper.xml</value>
                </array>
            </property>
            <!--注入别名相关配置   typeAliasesPackage:用来指定保重所有类起别名,默认的别名:类名|类名首字母小写-->
            <property name="typeAliasesPackage" value="com.eric.entity"></property>
        </bean>
    

    c. 创建DAO MapperScannerConfigurer 注入

  7. SqlSessionFactory 注入basePackage接口所在包;

        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlsessionFactory"/>
            <property name="basePackage" value="com.eric.dao"/>
        </bean>
    
  8. 编写DAO

  9. 开发Service接口

    public class UserServiceImpl implements UserSrevice{
        private UserDao userDao;
        private StudentGroupDao studentGroupDao;
        private DataSource dataSource;
        private PlatformTransactionManager platformTransactionManager;
    
        public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
            this.platformTransactionManager = platformTransactionManager;
        }
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public void setStudentGroupDao(StudentGroupDao studentGroupDao) {
            this.studentGroupDao = studentGroupDao;
        }
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public List<Users> findByName(String name) {
            return userDao.findByName(name);
        }
    
        @Override
        public List<Users> findAll() {
            return userDao.findAll();
        }
    
        @Override
        public List<Users> findById(String id) {
            return userDao.findById(id);
        }
    
        @Override
        public List<StudentGroup> findStuGroup(String name) {
            return studentGroupDao.findByName(name);
        }
    
        @Override
        public String studentAdd(StudentGroup studentGroup) {
            studentGroupDao.add(studentGroup);
            return "SUCCESS";
        }
    
        @Override
        public void add(Users users) throws SQLException {
            //控制事务,处理业务,调用业务
    //        users.setId(UUID.randomUUID().toString());
            //  创建事务配置对象
            DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
            //  获取事务状态
            TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);
            try {
                //控制事务
                dataSource.getConnection().setAutoCommit(false);//改为手动控制事务
                //处理业务
                userDao.add(users);
                //调用DAO
                dataSource.getConnection().commit();
            } catch (SQLException throwables) {
                dataSource.getConnection().rollback();
            }
        }
    }
    
  10. 开发Service实现类 注入:依赖DAO组件 依赖事务管理器 在对应增删改查方法中使用事务管理器控制事务

  11. 编写spring.xml

    a. 配置数据源事务管理器DataSourceTransactionManager 注入dataSource

        <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
    <!--        注入数据源对象-->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    

    b. 管理Service组件 注入DAO 和事务管理器

    <!--    管理Service组件-->
        <bean class="com.eric.service.UserServiceImpl" id="userService">
            <property name="studentGroupDao" ref="studentGroupDao"/>
            <property name="userDao" ref="userDao"/>
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
    
  12. 启动工厂测试

        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserServiceImpl userService = (UserServiceImpl) context.getBean("userService");
        System.out.println("-------start--------");
        userService.findAll().forEach(users -> System.out.println(users));
        System.out.println("-------FindById--------");
        System.out.println( userService.findById("5") );

spring中处理事务两种方式

  1. 编程式事务处理

定义:通过在业务层中注入事务管理器对象,然后通过编码的方式进行事务控制

缺点:代码冗余,不够通用,不便于维护

  1. 声明式事务处理

    定义:通过利用AOP切面编程进行事务控制,并对事务属性在配置文件中完成细粒度配置 ,这种方式称之为声明事务

    优点: 通用,减少代码冗余,更加专注于业务逻辑开发,无需重复编码

SM整合开发之service层事务优化

  1. 自定义完成事务管理

    1. 开发基于事务的通知(这个写法都是一样的,是一个框架)

      public class TransactionAdvice implements MethodInterceptor {
          private PlatformTransactionManager ptm;
          @Override
          public Object invoke(MethodInvocation invocation) throws Throwable {
              // 创建事务配置对象
              TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
              //获取事务状态
              TransactionStatus status = ptm.getTransaction(transactionDefinition);
              try {
                  Object result = invocation.proceed();//放行
                  ptm.commit(status);//提交事务
                  return result;
              } catch (Throwable throwable) {
                  ptm.rollback(status);
              }
              return null;
          }
      }
      
    2. 配置切面

      1. 配置通知对象

        <!--    配置通知对象-->
            <bean class="com.eric.inform.TransactionAdvice" id="transactionAdvice">
                <property name="ptm" ref="transactionManager"/>//事务管理器platformTransactionManager
            </bean>
        
      2. 配置切面

        <!--    配置切面-->
            <aop:config>
                <aop:pointcut id="pc" expression="within(com.eric.service.*.ServiceImpl)"/>
                <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
            </aop:config>
        
  2. spring框架开发声明式事务编程

    1. spring框架提供 tx:advice 标签(此标签是对上面代码TransactionAdvice的封装,相当于自己写了一个这样的事务通知类)

      作用:1、根据事务管理器在工厂创建一个事务环绕通知对象;2、tx:advice 标签可以对事务进行细粒度控制;

      <tx:advice id="创建的通知对象在工厂中唯一的标识" transactionManager="事务管理器是谁"></tx:advice>
      <!--举例-->
          <tx:advice transaction-manager="transactionManager" id="txAdvice">
              <!--事务细粒度配置-->
              <tx:attributes>
                  <tx:method name="add*"/><!--对应事务通知对象的方法名-->
                  <tx:method name="delete*"/>
                  <tx:method name="*insert"/>
              </tx:attributes>
          </tx:advice>
      
    2. 配置切面

      <!--    配置切面-->
          <aop:config>
              <aop:pointcut id="pc" expression="within(com.eric.service.*.ServiceImpl)"/>
              <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
          </aop:config>
      

总结:SM整合最终编程步骤

1.引入依赖

​ spring mybatis mysql mybatis-spring druid

2.建表

3.实体类

4.DAO接口

5.Mapper配置文件

6.Service接口

7.Service实现类

8.编写SM整合配置 spring.xml

  • a. 创建数据源

    <!--    创建DataSource-->
        <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/spbdemo"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
    
  • 创建SqlSessionFactory对象

    <!--    创建sqlSessionFactory-->
        <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
            <!--        依赖数据源-->
            <property name="dataSource" ref="dataSource"/>
    <!--        注入mapper文件通用方式-->
            <property name="mapperLocations" value="classpath:com/eric/mapper/*.xml"/>
    <!--        注入别名相关配置typeAliasesPackage:用来给指定包中所有类起别名  默认的别名:类名|类首字母小写-->
            <property name="typeAliasesPackage" value="com.eric.entity"/>
        </bean>
    
  • 创建DAO对象

        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlsessionFactory"/>
    <!--       扫描接口所在包-->
            <property name="basePackage" value="com.eric.dao"/>
        </bean>
    
  • 创建事务管理器

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
    <!--        注入数据源对象-->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
  • 根据事务管理器创建事务环绕通知 tx:advice

        <tx:advice transaction-manager="transactionManager" id="txAdvice">
            <!--事务细粒度配置-->
            <tx:attributes>
                <tx:method name="add*"/><!--对应事务,通知对象的方法名-->
                <tx:method name="delete*"/>
                <tx:method name="*insert"/>
            </tx:attributes>
        </tx:advice>
    
  • 配置事务切面

    <aop:config>        
          <aop:pointcut id="pc" expression="within(com.eric.service.*.ServiceImpl)"/>        
          <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>    
    </aop:config>
    

9.启动工厂测试Service对象

 ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserServiceImpl userService = (UserServiceImpl) context.getBean("userService");

spring中使用 log4j

作用:用来展示项目中运行日志

分类:项目根日志;项目子日志

日志级别:ERROR(高)>WARN>INFO(低)

引入依赖:log4j-core slf4j-log4j12

引入log4j.properties配置文件(需要放置在resources根目录下)

事务的传播属性

定义:在多个业务之间相互调用时,传递事务的过程称之为事务传播

​ 将事务对象在业务层之间进行传的过程

<!--    创建事务环绕通知并进行事务细粒度控制-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-------------------------------------------------------------------------------------------
            propagation:事务传播属性(不配置没有任何事务)
                     REQUIRED: 需要事务(如果外层没有事务,则开启新的事务;如果外层存在事务,则加入当前事务)
                     SUPPORTS: 支持事务 (如果外层没有事务,不会开启新的事务;如果外层存在事务,则加入当前事务)
                 REQUIRES_NEW: 每次开启新的事务(如果存在外层事务,那么外部事务挂起,自己开启新的事务执行,执行完毕后恢复外层事务继续执行)
                NOT_SUPPORTED:不支持事务(如果存在外层事务,外层事务挂起;自己以非事务方式执行,执行完成,恢复外部事务继续执行)
                        NEVER:不能有事务(存在事务报错)
                    MANDATORY: 没有事务报错
                       NESTED:事物之间可以嵌套运行(mysql,oracle不支持)
---------------------------------------------------------------------------------------------
           isolation:  数据库隔离级别
                    DEFAULT:  使用数据库默认的隔离级别(推荐)
           READ_UNCOMMITTED:  读未提交   一个客户端读到了另一个客户端没有提交的数据(脏读现象)
             READ_COMMITTED:  读提交     一个客户端只能读到另一个客户端提交的数据 (避免脏读现象)
            REPEATABLE_READ:  可重复读   主要是用来避免不可重复读现象出现  (行锁)
               SERIALIZABLE: 序列化读    主要是用来避免幻影读现象出现     (表锁)
               注意:隔离级别越高,查询效率越低(推荐使用数据库默认级别)
---------------------------------------------------------------------------------------------
          read-only:  事务读写性  true 只读,不能执行增删改操作  false可读可写(mysql支持,oracle不支持)
              rollback-for:  出现什么异常回滚  默认出现RuntimeException 机器子类异常回滚
           no-rollback-for:  出现什么异常不会出现回滚   java.lang.RuntimeException

          timeout: 事务超时性 -1永不超时(设置>0正整数  指设置超时时间 单位秒)
-------------------------------------------------------------------------------------------->
        <tx:attributes>
            <tx:method name="insert" propagation="MANDATORY" timeout="-1" no-rollback-for="java.lang.RuntimeException" isolation="SERIALIZABLE" read-only="true"/>
            <tx:method name="findAll" propagation="NOT_SUPPORTED"/>
        </tx:attributes>
    </tx:advice>

总结:spring+struts2框架整合 /ss整合(Struts2 是用来管理Action信息)

  1. 引入依赖:spring struts2-core<-版本要对应->structs2-spring-plugin.jar servlet-api jst1

  2. 配置web.xml

    1. a.配置spring启动工厂监听器

        <!--  配置启动工厂的监听器listener-->
        <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
      
    2. b.配置spring监听器读取工厂配置文件的位置和名称

        <!--  配置spring配置文件为止-->
        <context-param>
          <param-name>contextConfigLocation</param-name><!--此处固定格式-->
          <param-value>classpath:spring.xml</param-value>
        </context-param>
      
    3. c.配置struts2核心filter

      <!--  filter-->
        <filter>
          <filter-name>struts</filter-name>
          <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
        </filter>
        <filter-mapping>
          <filter-name>struts</filter-name>
          <url-pattern>/*</url-pattern>
        </filter-mapping>
      
  3. 引入struts.xml,必须在resources根目录下,名字固定

  4. 开发Action组件

    public class UserAction {
        public String hello(){
            System.out.println("Helo Spring Struts");
            return Action.SUCCESS;
        }
    }
    
  5. 引入spring.xml并通过工厂管理Action组件

    <bean class="com.eric.action.UserAction" id="userAction" scope="prototype"></bean><!--scope属性值必须prototype多例形式-->
    
  6. 配置struts.xml

        <package name="user" extends="struts-default" namespace="/user">
            <action name="hello" class="userAction" method="hello"><!--class属性的值是spring.xml中的id;如小标题5↑-->
                <result name="success">/index.jsp</result>
            </action>
        </package>
    
  7. 部署服务器访问action测试

SSM整合步骤 Mybatis + Spring + Struts2

  1. 引入依赖

    spring 相关 mybatis mybatis-spring mysql druid struts2相关 struts2-spring-plugin servlet-api jstl fastjson

  2. SM => spring + mybatis

    1. )建表

    2. )实体类

    3. )DAO接口

    4. )Mapper配置文件

    5. )Service接口

    6. )Service实现类 一般依赖DAO组件,所以要注入DAO组件

    7. )编写spring.xml

      1. 创建数据源对象DruidDataSource 注入driverClassName url username password

        <!--创建DataSource-->
            <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/spbdemo"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </bean>
        
      2. 创建SqlSessionFactory 注入dataSource mapperLocations typrAliasPackages别名

        <!--创建sqlSessionFactory-->
            <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
                <!--依赖数据源-->
                <property name="dataSource" ref="dataSource"/>
        		<!--注入mapper文件通用方式-->
                <property name="mapperLocations" value="classpath:com/eric/mapper/*.xml"/>
        		<!--注入别名相关配置typeAliasesPackage:用来给指定包中所有类起别名  默认的别名:类名|类首字母小写-->
                <property name="typeAliasesPackage" value="com.eric.entity"/>
            </bean>
        
      3. 创建DAO对象 MapperScannerConfigur 注入 sqlSessionFactory basePackage

            <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
                <property name="sqlSessionFactoryBeanName" value="sqlsessionFactory"/>
        		<!--扫描接口所在包-->
                <property name="basePackage" value="com.eric.dao"/>
            </bean>
        
      4. 创建事务管理器对象 DataSourceTransactionManager 注入DataSource

        <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        		<!--注入数据源对象-->
                <property name="dataSource" ref="dataSource"/>
            </bean>
        
      5. 创建安事务环绕通知并对事务进行事务细粒度控制<tx:advice id=" " transaction-manager="事务管理器">

            <tx:advice transaction-manager="transactionManager" id="txAdvice">
                <!--事务细粒度配置-->
                <tx:attributes>
                    <tx:method name="add*"/><!--对应事务通知对象的方法名-->
                    <tx:method name="delete*"/>
                    <tx:method name="*insert"/>
                </tx:attributes>
            </tx:advice>
        
      6. 配置事务切面:< aop:config >

        <aop:config>        
              <aop:pointcut id="pc" expression="within(com.eric.service.*.ServiceImpl)"/>        
              <aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>    
        </aop:config>
        
      7. 管理Service组件对象 并 注入响应DAO组件

        <!--管理Service组件-->
            <bean class="com.eric.service.XXX ServiceImpl" id="XXX Service">
                <property name="XXX Dao" ref="XXX Dao"/>
                <property name="XXX Dao" ref="XXX Dao"/>
                <property name="dataSource" ref="dataSource"/>
            </bean>
        
        
  3. SS => spring+struts2

    1. )配置web.xml

      1. 配置启动工厂的监听器 ContextLoaderListener

          <!--  配置启动工厂的监听器listener-->
          <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
        
      2. 配置spring.xml 配置文件位置 context-param key:contextConfigLocation value:spring配置文件位置

          <!--  配置spring配置文件为止-->
          <context-param>
            <param-name>contextConfigLocation</param-name><!--此处固定格式-->
            <param-value>classpath:spring.xml</param-value>
          </context-param>
        
      3. 配置 struts2 核心 filter StrutsPrepareAndExecuteFilter url=pattern:/*

        <!--  filter-->
          <filter>
            <filter-name>struts</filter-name>
            <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
          </filter>
          <filter-mapping>
            <filter-name>struts</filter-name>
            <url-pattern>/*</url-pattern>
          </filter-mapping>
        
    2. )引入struts.xml

    3. )开发Action组件 注入Service组件(收集数据[配置成员变量],调业务对象,流程跳转[配置result])

    4. )编写spring配置文件 管理Action组件

      <bean class="com.eric.action.XXX Action" id="XXX Action" scope="prototype">
          <property name="XXX Service" ref="XXX Service"/>
      </bean><!--scope属性值必须prototype多例形式-->
      
    5. )配置struts.xml

          <package name="user" extends="struts-default" namespace="/user">
              <action name="hello" class="userAction" method="hello"><!--class属性的值是spring.xml中的id;如小标题4↑-->
                  <result name="success">/index.jsp</result>
              </action>
          </package>
      
  4. 项目部署服务器,进行访问

    访问路径:http://localhost:端口号/项目名/namespace/action标签的name属性

spring+mybatis+struts2项目编码实现

spring+mybatis+struts2整合

使用url传递参数,向user表中添加数据

Spring框架的注解式开发

  1. 注解(Annotation)式开发

    定义:通过Spring框架提供的一系列相关注解完成项目中快速开发

    注解:Annotation是加吧中国一重特殊类,类似于interface 使用时:@注解类名(属性=参数)例如@Param

  2. spring 中注解

    前置条件:必须在工厂配置文件中完成注解扫描

    1). 创建对象相关注解

    	<!--开启注解扫描-->
      <context:component-scan base-package="com"/>
    
  3. 实例化相关注解

    1. @Component(value="beanid")
      修饰范围: 用在类上
      注解作用: 通用的创建实例的注解,用来创建当前这个类的实例
      value属性: 用来指定创建的对象在工厂中的唯一标识(可省略value直接写标识)
      如果不指定默认创建对象在工厂中的标识为类名首字母小写
    2. @Repository
      修饰范围: 用在类上
      注解作用: @component的子类注解专用于DAO组件的创建,
      通常加在DAO组件上
      value属性: 用来指定创建的对象在工厂中的唯一标识
      如果不指定默认创建对象在工厂中的标识为类名首字母小写
    3. @Service
      修饰范围: 用在类上
      注解作用: @component的子类注解专用于Service组件的创建,
      通常加在Service组件上
      value属性: 用来指定创建的对象在工厂中的唯一标识
      如果不指定默认创建对象在工厂中的标识为类名首字母小写
    4. @Controller
      修饰范围: 用在类上
      注解作用: @component的子类注解专用于Action组件的创建,
      通常加在Action组件上
      value属性: 用来指定创建的对象在工厂中的唯一标识
      如果不指定默认创建对象在工厂中的标识为类名首字母小写
  4. 控制对象的创建次数的注解

    1. @Scope(value="singleton|prototype")
      修饰范围: 用在类上
      注解作用: 用来控制这个实例在工厂中的创建次数
      value属性: singleton为单例,prototype为多例 默认单例
  5. 属性注入相关的注解

    1. @Autowired(Spring提供)
      修饰范围: 用在成员变量或成员变量的GET/SET方法上
      注解作用: 用来给类中成员变量赋值
      注入原则: 默认根据类型自动注入
    2. @Resource(JAVAEE提供)
      修饰范围: 用在成员变量或成员变量的GET/SET方法上
      注解作用: 用来给类中成员变量赋值
      注入原则: 默认根据名称自动注入名称找不到根据类型自动注入
  6. 控制事务的相关注解

    1. @Transactional
      修饰范围: 用在类上主要用在业务层组件类上或者是方法上
      注解作用: 用来给类中方法加入事务,当类上和方法上同
      时存在该注解时局部优先
      注解属性:
      propagation 用来控制传播属性0
      Isolation 用来控制隔离级别
      timeout 用来设置超时性
      rollback-for 用来设置什么异常回滚
      norollback-for 用来设置什么异常不会滚
      readonly 用来设置事务读写性

    2. 注意:如果要使用事务注解在配置文件中必须开启事务注解生
      效加入如下配置:

      <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
      
posted @ 2021-10-23 16:50  二十八畫生  阅读(32)  评论(0)    收藏  举报