Spring

作者: Roct | 来源:发表于2019-08-13 23:10 被阅读0次

    Spring介绍

    Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益
    简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

    Spring好处

    方便解耦,简化开发:
    • Spring就是一个大工厂,专门负责生成Bean,可以将所有对象创建和依赖关系维护由Spring管理
    AOP编程的支持:
    • Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
    声明式事务的支持:
    • 只需要通过配置就可以完成对事务的管理,而无需手动编程
    方便程序的测试:
    • SpringJunit4支持,可以通过注解方便的测试Spring程序
      方便集成各种优秀框架:
    Spring不排斥各种优秀的开源框架
    • 其内部提供了对各种优秀框架(如:StrutsHibernateMyBatisQuartz等)的支持
    降低JavaEE API的使用难度Spring:
    • JavaEE开发中一些难用的APIJDBCJavaMail、远程调webservice用等),都提供了封装,使这些API应用难度大大降低

    Spring体系结构

    Spring 框架是一个分层架构,,它包含一系列的功能要素并被分为大约20个模块。这些模块分为Core ContainerData Access/IntegrationWebAOP(Aspect Oriented Programming)Instrumentation和测试部分,如下图所示:

    Spring架构
    Spring在项目中的架构
    • web层:Struts,SpringMVC
    • dao层:Hibernate,mybatis
      Spring在项目中的架构

    Spring IOC的底层实现原理

    Spring IOC的底层实现原理

    Spring核心jar包

    • spring-core-3.2.2.RELEASE.jar
      包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心。

    • spring-beans-3.2.2.RELEASE.jar
      所有应用都要用到的,它包含访问配置文件、创建和管理bean
      以及进行Inversion of Control(IoC) / Dependency Injection(DI)操作相关的所有类

    • spring-context-3.2.2.RELEASE.jar
      Spring提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,
      如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等。

    • spring-expression-3.2.2.RELEASE.jar
      Spring表达式语言

    • com.springsource.org.apache.commons.logging-1.1.1.jar
      第三方的主要用于处理日志

    Spring IOC/DI

    • IOC Inverse of Control反转控制的概念, 就是将原本在程序中手动创建对象的控制权, 交给Spring框架管理, 简单的说, 就是将创建对象控制权被翻转到了Spring框架
    • DI Dependency Injection依赖注入的概念, 就是在Spring创建这个对象的过程中, 将这个对象所依赖的属性注入进去

    简单的Spring框架使用

    创建一个UserService接口

    public void sayHello();
    

    和它的实现类UserServiceImpl

    public void sayHello {
        private String name;
        public String getName() {
              return name;
         }
        public void setName(name) {
              this.name = name;
        }
        public void sayHello() {
              System.out.println("Spring hello" + name)
        }
        
    }
    
    • 传统使用UserService
    public void demo() {
        UserService userService = new UserServiceImpl();
        userService.setName("李四");
        userService.sayHello();
    }
    

    输出结果

    Spring hello李四
    
    • 使用Spring
      在resource里创建一个applicationContext.xml文件, 里面写入
    // IOC
    <bean id = "userService" class="com.rui.ioc.demo.UserServiceImpl>
      <property name = "name" value="李四" />
    </bean>
    
    public void demo () {
        // 创建Spring工厂, 加载classPath下的配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 通过工厂获得类
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.sayHello();
    }
    

    输出结果

    Spring hello李四
    
    • IOC=>
    使用`Spring`工厂获得类叫做`IOC`, 即将创建对象的权利交给Spring
    
    • DI =>
    <property name = "name" value="李四" />
    

    叫做依赖注入, 就是在Spring创建这个对象的过程中, 将这个对象所依赖的属性注入进去

    Spring的工厂类

    Spring的工厂类
    • ClassPathXmlApplicationContextBeanFactory的区别

      • BeanFactoryspring中比较原始,比较古老的Factory。因为比较古老,所以BeanFactory无法支持spring插件,例如:AOPWeb应用等功能。
      • 如果使用ApplicationContext,如果配置的beansingleton,那么不管你有没有或想不想用它,它都会被实例化。好处是可以预先加载,坏处是浪费内存。
      • BeanFactory,当使用BeanFactory实例化对象时,配置的bean不会马上被实例化,而是等到你使用该bean的时候(getBean)才会被实例化。好处是节约内存,坏处是速度比较慢。多用于移动设备的开发。
      • 没有特殊要求的情况下,应该使用ApplicationContext完成。因为BeanFactory能完成的事情,ApplicationContext都能完成,并且提供了更多接近现在开发的功能。
    • 加载某个位置下的配置文件

    public void demo () {
      // 创建Spring工厂
      ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:\\applicationContext.xml");
      // 通过工厂获得类
      UserService userService = (UserService) applicationContext.getBean("userService");
      userService.sayHello();
    }
    

    Spring的Bean管理方式

    • XML

      • 三种实例化Bean的方式

        • 使用类构造器实例化(默认无参数)
      // 对象
      public class Bean1 {
          public Bean1() {
              System.out.println("Bean1被实例化了...");
          }
      }
      
      // Spring配置文件
      <bean id="bean1" class="com.rui.demo.Bean1" />
      
      @Test
      public void demo () {
        // 创建Spring工厂, 加载classPath下的配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean1 bean1 = (bean1) applicationContext.getBean("bean1");
      }
      
      输出结果为
      Bean1被实例化了...
      
      • 使用静态工厂实例化(简单工厂模式)
      // 对象
      public class Bean2 {
          public Bean2() {
              System.out.println("Bean2被实例化了...");
          }
      }
      
       // Bean2的静态工厂
      public class Bean2Factory {
          public static Bean2 createBean2() {
              System.out.println("Bean2被实例化了...");
              return new Bean2();
          }
      }
      
      <bean id="bean2" class="com.rui.demo.Bean2Factory"  factory-method="createBean2" />
      
      public void demo () {
        // 创建Spring工厂, 加载classPath下的配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean2 bean2 = (bean2) applicationContext.getBean("bean2");
      }
      
      输出结果为
      Bean2被实例化了...
      
      • 使用实例工厂方法实例化(工厂方法模式)
      // 对象
      public class Bean3 {
          public Bean3() {
              System.out.println("Bean3被实例化了...");
          }
      }
      
       // Bean3的实例化工厂方法
      public class Bean3Factory {
          public Bean3 createBean3() {
              System.out.println("Bean3被实例化了...");
              return new Bean3();
          }
      }
      
      <bean id="bean3Factory" class="com.rui.demo.Bean3Factory" />
      <bean id="bean3" actory-bean="bean3Factory"  factory-method="createBean3" />
      
      public void demo () {
        // 创建Spring工厂, 加载classPath下的配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean3 bean3 = (bean3) applicationContext.getBean("bean3");
      }
      
      输出结果为
      Bean3被实例化了...
      
      • Bean的常用配置

        • id和name
          • 一般情况下, 装配一个Bean的时候, 通过一个id属性作为Bean的名称
          • id属性在IOC容器里必须是唯一的
          • 如果Bean的名称中含有特殊字符, 就需要使用name属性
        • class
          • class用于设置一个类的完全路径名称, 主要作用是IOC容器生成类的实例
        • Bean的作用域Scope
        类别 说明
        singleton(默认) SpringIOC容器中仅存在一个Bean实例, Bean以单例的方式存在
        prototype 每次调用getBean()时都返回一个新的实例
        request 每次Http请求都会创建一个新的Bean, 该作用域仅适用于WebApplicationContext环境
        Session 同一个HTTP Session共享一个Bean, 不同的HTTP Session使用不同的Bean, 该作用域仅适用于WebApplicationContext环境
      • Spring容器中Bean的生命周期

        Spring初始化bean或销毁bean的时候, 有时候需要做一些处理工作, 因此Spring可以在创建和销毁bean的时候调用bean的两个声明周期方法

        类别 说明
        init-method 初始化方法
        destory-method 销毁方法, 仅在scope=singleton有效
          // 当bean被载入到容器的时候调用init, 当bean从容器中删除的时候调用destory(仅在`scope=singleton有效)
            <bean id="xxx" class="xxxx" init-method="init" destory-method="destory" />
        
        Spring容器中Bean的生命周期
        1. instantiate bean对象实例化
        2. populate properties 封装对象
        3. 如果Bean实现BeanNameAware执行setBeanName
        4. 如果Bean实现BeanFactoryAware或者ApplicationContextAware设置工厂
        5. 如果存在类实现BeanPostProcessor(后处理Bean), 执行postProcessBeforeInitialization
        6. 如果Bean实现InitializingBean执行afterPropertiesSet
        7. 调用<bean init-method="init" />指定初始化init
        8. 如果存在类实现BeanPostProcessor(处理Bean), 执行postProcessAfterInitiazation
        9. 执行业务逻辑
        • Spring中beanPostProcessor使用案例
          • applicationContext.xml
          <bean class="com.rui.ioc.MyBeanPostProcessor" />
          <bean id="userDao" class="com.rui.ioc.UserDaoImpl" />
          
          • UserDao
            public interface UserDao {
                public void findAll();
                public void save();
                public void update();
                public void delete();
             }  
          
          • UserDaoImpl
           public class UserDaoImpl implements UserDao {
            @Override
            public void findAll() {
            System.out.println("查");
            }
            @Override
             public void save() {
                System.out.println("存");
              }
            @Override
            public void update() {
                System.out.println("改");
             }
            @Override
            public void delete() {
                System.out.println("删");
            }
          }
          
          • MyBeanPostProcessor
             public class MyBeanPostProcessor implements BeanPostProcessor {
               @Override
               public Object postProcessBeforeInitialization(Object bean, String beanName)
             throws BeansException {
                     return bean;
             }
                @Override
                public Object postProcessAfterInitialization(final Object bean, String beanName)
               throws BeansException {
                   if ("userDao".equals(beanName)) {
                         Object proxy =
                     java.lang.reflect.Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                             bean.getClass().getInterfaces(), new InvocationHandler() {
                                 public Object invoke(Object proxy, Method method, Object[] args)
                                         throws Throwable {
                                     if ("save".equals(method.getName())) {
                                         System.out.println("权限调用");
                                         return method.invoke(bean, args);
                                     }
                                     return method.invoke(bean, args);
                                 }
                             });
                     return proxy;
                 } else {
                     return bean;
               }
             }
          }
          
          • 输出
             第八步2222
             查
             权限调用
             存
             改
             删
            
      • Spring的属性注入

        • 构造函数注入, 在对象中, 必须实现对应的构造函数参数为设置的属性
          <bean id="user" class="com.rui.demo.User">
             <contructor-arg name="name" value="1223">
             <contructor-arg name="age" value="23">
          </bean>
          
        • 属性setter方法注入, 在Spring配置文件中, 通过<property>设置注入的属性, 在对象中必须实现对应的参数的setter方法
          <bean id="user" class="com.rui.demo.User">
             <property name="name" value="1223">
             <property name="age" value="23">
          </bean>
           ```
           ```
          // User对象中含有一个对象Cat
          <bean id="user" class="com.rui.demo.User">
             <property name="name" value="1223">
             <property name="age" value="23">
             <property name="cat" ref="cat"> // ref引入别的bean的id
          </bean>
          <bean id="cat" class="com.rui.demo.Cat">
             <property name="name" value="ketty">
          </bean>     
           ```
          
        • P名称空间注入
           <!--xmlns:p="http://www.springframework.org/schema/p"         需要引入-->
           <bean id="userDao" class="com.rui.ioc.UserDaoImpl" p:age="12"
           p:name="shsj" />
          
        • SpEL注入
            <bean id="catogery" class="com.rui.ioc.Category">
                <property name="name" value="#{'服装'}" />
            </bean>
            <bean id="productInfo" class="com.rui.ioc.ProductInfo" />
            <bean id="productInfo" class="com.rui.ioc.Product">
                 <property name="name" value="#{'男装'}" />
                 <!--        <property name="price" value="#{'1999'}" />-->
                 <property name="price" value="#{productInfo.caculatePrice}" />
                <property name="category" value="#{catogery}" />
            </bean>
          
        • 复杂类型的属性注入(一般用于整合其他框架)
        // 数组类型
        <property name="arrs">
            <list>
                <value>aaa</value>
            </list>
        </property>
        // list类型
        <property name="list">
            <list>
                <value>aaa</value>
            </list>
        </property>
        // 数组类型
        <property name="arrs">
            <list>
                <value>aaa</value>
            </list>
        </property>
        // set类型
        <property name="set">
            <set>
                <value>aaa</value>
            </set>
        </property>
        // Map类型
        <property name="map">
            <set>
                <value>aaa</value>
            </set>
        </property>
        // Map类型
        <property name="arrs">
            <map>
                <entry key="aaa" value="bbb" />
            </map>
        </property>
        // properties类型
        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">12345</prop>
            </props>
        </property>
        

    Spring的Bean注解方式

    • Bean的管理

    applicationContext.xml文件里, 引入

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <context:component-scan base-package="com.rui.ioc"/>
    </beans>
    

    就会自动扫描ioc下所有的类, 在类上标注@Component/@Service/@Repository/@Controller

    1、@controller 控制器(注入服务)
    2、@service 服务(注入dao
    3、@repository dao(实现dao访问)
    4、@component (@component("xxx"))(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="xxx" class=""/>

    • 属性的注入-注解方式

      • 简单属性注入
         @value("coding")
         private String something; // something输出值为coding
        
      • @Autowired进行自动注入
        • @Autowired默认按照类型进行注入
          • 如果两个相同的Bean类型相同, 则按照名称进行注入
        • 通过@Autowiredrequired属性, 设置一定要找到匹配的Bean
        • 使用@Qualifier指定注入Bean的名称
          同时使用@Autowired@Qualifier可以指定固定的Bean, 效果和使用@Resource相同
      • @PostConstruct: 初始化
      • @PreDestroy: 销毁
      • @Scope("prototype"): 多例, 默认为单例

    Spring AOP

    • 什么是AOP

      AOP Aspect Oriented Programing 面向切面编程
      AOP采取横向抽取机制, 取代了传统纵向继承体系重复性代码(性能监视, 事务管理, 安全检查, 缓存)

    • AOP术语

      AOP术语
    • JDK动态代理

    UserDao

    package com.rui.aop;
    
    public interface UserDao {
        public void findAll();
        public void save();
        public void update();
        public void delete();
    }
    

    UserDaoImpl

    package com.rui.aop;
    public class UserDaoImpl implements UserDao {
        @Override
        public void findAll() {
            System.out.println("查找用户~");
        }
        @Override
        public void save() {
            System.out.println("保存用户~");
        }
        @Override
        public void update() {
            System.out.println("更新用户~");
        }
        @Override
        public void delete() {
            System.out.println("删除用户~");
        }
    }
    

    MyJdkProxy

    package com.rui.aop;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    public class MyJDKProxy implements InvocationHandler {
        private UserDao userDao;
        public MyJDKProxy(UserDao userDao) {
            this.userDao = userDao;
        }
        public Object createProxy() {
            Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao
                    .getClass().getInterfaces(), this);
            return proxy;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("save".equals(method.getName())) {
                System.out.println("保存用户之前的操作");
                return method.invoke(userDao, args);
            }
            return method.invoke(userDao, args);
        }
    }
    

    aopTest

    package com.rui.aop;
    import org.junit.Test;
    public class aopTest {
        @Test
        public void demo1() {
            UserDao userDao = new UserDaoImpl();
            userDao = (UserDao)new MyJDKProxy(userDao).createProxy();
            userDao.findAll();
            userDao.delete();
            userDao.save();
            userDao.update();
        }
    }
    

    输出结果

    查找用户~
    删除用户~
    保存用户之前的操作
    保存用户~
    更新用户~
    
    • 使用CGLIB生成代理

    MyCglibProxy

    public class MyCglibProxy implements MethodInterceptor {
        private ProductDao productDao;
    
        public MyCglibProxy(ProductDao productDao) {
            this.productDao = productDao;
        }
        public Object createProxy() {
            // 1. 创建核心类
            Enhancer enhancer = new Enhancer();
            // 2. 设置父类
            enhancer.setSuperclass(productDao.getClass());
            // 3. 设置回调
            enhancer.setCallback(this);
            // 4.生成代理
            Object proxy = enhancer.create();
            return proxy;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            if ("save".equals(method.getName())) {
                System.out.println
                        ("============================权限校验==========================");
                return methodProxy.invokeSuper(o, objects);
            }
            return methodProxy.invokeSuper(o, objects);
        }
    }
    

    ProductDao

    public class ProductDao {
        public void findAll() {
            System.out.println("查找用户~");
        }
        public void save() {
            System.out.println("保存用户~");
        }
        public void update() {
            System.out.println("更新用户~");
        }
        public void delete() {
            System.out.println("删除用户~");
        }
    }
    

    Test

    @Test
    public void demo1() {
         ProductDao productDao = new ProductDao();
         ProductDao proxy = (ProductDao)new MyCglibProxy(productDao).createProxy();
         proxy.findAll();
         proxy.delete();
         proxy.save();
         proxy.update();
    }
    

    输出

    查找用户~
    删除用户~
    ============================权限校验==========================
    保存用户~
    更新用户~
    

    代理相关

    • Spring在运行期, 生成动态代理对象, 不需要特殊的编译器
    • Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术为目标Bean执行横向织入
      • 若目标对象实现了若干接口, Spring使用JDKjava.lang.reflect.Proxy类代理
      • 若目标对象没有实现任何接口, spring使用cglib库生成目标对象的子类
    • 程序中应该优先对接口创建代理, 便于程序解耦维护
    • 标记为final的方法, 不能被代理, 因为无法覆盖
      • JDK动态代理, 是针对接口生成子类, 接口中方法不能使用final修饰
      • CGLib是针对目标类生产子类, 因此类或方法, 不能使用final
    • Spring只支持方法连接点, 不提供属性连接点

    Spring AOP增强类型

    • AOP联盟为通知Advice定义了org.aopallinance.aop.Interface.Advice
    • Spring按照通知Advice在目标类方法的链接点位置, 可以分为5类
      • 前置通知 org.springframework.aop.MethodBeforeAdvice
        • 在目标方法执行前实施增强
      • 后置通知 org.springframework.aop.AfterReturningAdvice
        • 在目标方法执行后实施增强
      • 环绕通知 org.springframework.intercept.MethodInterceptor
        • 在目标方法执行前后都实施增强
      • 异常抛出通知 org.springframework.ThrowsAdvice
        • 在方法抛出异常后实施增强
      • 引介通知 org.springframework.IntroductionInterceptor
        • 在目标类中添加一些新的方法和属性

    Spring AOP切面类型

    • Advisor: 代表一般切面, Advice本身就是一个切面, 对目标类所有方法进行拦截
    • PointcutAdvisor: 代表具有切点的切面, 可以指定拦截目标类哪些方法
    • IntroductionAdvisor: 代表引介切面, 针对引介通知而使用切面

    自动创建代理

    • BeanNameAutoProxyCreator 根据Bean名称创建代理
    • DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理
    • AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ注解进行自动代理

    AspectJ AOP

    • AspectJ是一个基于java语言的AOP框架
    • Spring 2.0以后新增了对AspectJ切点表达式的支持

    1.Spring开启Aspectj

     <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:aop="http://www.springframework.org/schema/aop"
            xsi:schemaLocation="http://www.springframework.org/schema/bea      ns  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">
     
       <!--    开启aspectj的注解开发-->
       <aop:aspectj-autoproxy/>
     </beans>
    

    2.@AspectJ提供不同的通知类型

    • @Before 前置通知, 相当于BeforeAdvice
    • @AfterReturning 后置通知, 相当于AfterReturningAdvice
    • @Around 环绕通知, 相当于MethodInterceptor
    • @AfterThrowing 异常跑出通知, 相当于ThrowAdvice
    • @After 最终final通知, 不管是否异常, 该通知都会执行
    • @DeclareParents 引介通知, 相当于IntroductionInterceptor

    3. 在通知中通过value属性定义切点

    • 通过excution函数, 可以定义切点的方法切入
    • 语法
    excution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
    
    • eg.
    - 匹配所有类public方法 excution(public * *(...))
    - 匹配指定包下所有类方法 excution(* com.imooc.dao.*(...)) 不包含子包
    - 匹配指定包下所有类方法 excution(* com.imooc.dao..*(...)) 包含子包
    - 匹配指定类所有方法 excution(* com.imooc.service.UserService.*(..))
    - 匹配实现特定接口所有类方法 excution(* com.imooc.service.dao.GenericDAO+.*(..))
    - 匹配所有save开头的方法 excution(* save*(..))
    

    4. @Before/@AfterReturning/@Around/AfterThrowing/After

    • 切面类
    @Aspect
    public class MyAspect {
      @Pointcut(value="execution(* com.rui.aspectj.ProductDao.save(..))")
      private void save() {}
    
      @Pointcut(value = "execution(* com.rui.aspectj.ProductDao.update(..))")
      private void update() {}
    
      @Pointcut(value = "execution(* com.rui.aspectj.ProductDao.delete(..))")
      private void delete() {}
    
      @Before(value="save()")
      public void before(JoinPoint joinPoint) {
          System.out.println("前置通知============" + joinPoint);
      }
      @AfterReturning(value="update()",
      returning = "result")
      public void afterReturning(Object result) {
          // result为返回值
          System.out.println("后置通知============" + result);
      }
      @Around(value="delete()")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
          System.out.println("环绕前通知=====================");
          Object obj = joinPoint.proceed(); // 执行目标方法
          System.out.println("环绕后通知=====================");
          return obj;
      }
      @AfterThrowing(value="execution(* com.rui.aspectj.ProductDao.findOne(..))",
      throwing = "e")
      public void afterThrowing(Throwable e) {
          System.out.println("异常抛出通知===========" + e.getMessage());
      }
      @After(value = "execution(* com.rui.aspectj.ProductDao.findAll(..))")
      public void after() {
          System.out.println("最终通知!================");
      }
    }
    
    • Dao
    public class ProductDao {
      public void save() {
          System.out.println("保存商品...");
      }
      public String update() {
          System.out.println("更新商品...");
          return "hello world";
      }
      public void delete() {
          System.out.println("删除商品...");
      }
      public void findOne() {
          System.out.println("查找一个商品...");
      }
      public void findAll() {
          System.out.println("查找所有商品...");
      }
    }
    
    • Spring 配置
    <!--    开启aspectj的注解开发-->
      <aop:aspectj-autoproxy/>
    <!--    目标类-->
      <bean id="productDao" class="com.rui.aspectj.ProductDao" />
      <!--定义切面-->
      <bean class="com.rui.aspectj.MyAspect" />
    
    • 测试代码
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AspectTest {
      @Resource(name = "productDao")
      private ProductDao productDao;
      @Test
      public void demo1() {
          productDao.save();
          productDao.delete();
          productDao.findAll();
          productDao.findOne();
          productDao.update();
      }
    }
    
    • output
    前置通知============execution(void com.rui.aspectj.ProductDao.save())
    保存商品...
    环绕前通知=====================
    删除商品...
    环绕后通知=====================
    查找所有商品...
    最终通知!================
    查找一个商品...
    异常抛出通知===========/ by zero
    

    JDBC Template

    使用

    • Mysql驱动
    • Spring组件(Core, beans, context, aop)
    • JDBCTemplate(jdbc, tx)
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://local........." />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="datasource" ref="datasource" />
    </bean>
    

    具体方法

    • update方法
    // 对数据进行增删改操作
    int update(String sql, Object[] args)
    int update(String sql, Object... args)
    
    public void testUpdate() {
        String sql = "insert into student(name, sex) values(?,?)";
        jdbcTemplate.update(sql, new Object[]{"zhagsan", "man"});
        // 或
        jdbcTemplate.update(sql, "zhagsan", "man");
    }
    
    • batchUpdate方法
    // 对数据进行批量增删改操作
    int [] batchUpdate(String [] sql)
    int [] batchUpdate(String sql, List<Object[]> args)
    
    public void testBatchUpdate() {
        String[] sqls = {
                  "insert into student(name, sex) values ('关羽', '男')",
                  "insert into student(name, sex) values ('张飞', '男')"
        };
        jdbcTemplate.batchUpdate(sqls);
    }
    
    public void testBatchUpdate() {
        String sql = insert into student(name, sex) values (?, ?)";
        List<Object []> list = new ArrayList<Object[]>();
        list.add(new Object[]{"关羽", "男"});
        list.add(new Object[]{"张飞", "男"});
        jdbcTemplate.batchUpdate(sql, list);
    }
    
    • 查询简单数据项

      • 获取一个
      T queryForObject(String sql, Class<T> type)
      T queryForObject(String sql, Object[] args, Class<T> type)
      T queryForObject(String sql, Class<T> type, Object... args)
      
      public void testQuerySimple() {
          String sql = "select count(*) from student";
          int count = jdbcTemplate.queryForObject(sql, Integer.class);
      }
      
      • 获取多个
      List<T> queryForList(String sql, Class<T> type)
      List<T> queryForList(String sql, Object[] args, Class<T> type)
      List<T> queryForList(String sql, Class<T> type, Object... args)
      
      public void testQuerySimple() {
          String sql = "select name from student where sex=?";
          List<String> names = jdbcTemplate.queryForList(sql, String.class, "男");
      }
      
    • 查询复杂对象(封装为Map)

      • 获取一个
      Map queryForMap(String sql)
      Map queryForMap(String sql, Object[] args)
      Map queryForMap(String sql, Object... args)
      
      public void testMap() {
          String sql = "select * from student where id = ?";
          Map<String, Object> stu = jdbcTemplate.queryForMap(sql, 1003);
      }
      
      • 获取多个
      List<Map<String, Object>> queryForList(String sql)
      List<Map<String, Object>>queryForList(String sql, Object[] args)
      List<Map<String, Object>>queryForList(String sql, Object... args)
      
      public void testMap() {
          String sql = "select * from student";
          Map<String, Object> stu = jdbcTemplate.queryForMap(sql);
      }
      
    • 查询复杂对象(封装为实体对象)

      • RowMapper接口
        • 获取一个
        T queryForObject(String sql, RowMapper<T> mapper)
        T queryForObject(String sql, Object[] args, RowMapper<T> mapper)
        T queryForObject(String sql, RowMapper<T> mapper, Object... arg)
        
        public void testQueryEntity() {
            String sql = "select * from student where id = ?";
            Student stu = jdbcTemplate.queryForObject(sql, new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throw SQLException {
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("id"));
                    stu.setName(resultSet.getInt("name"));
                    stu.setSex(resultSet.getInt("Sex"));
                    stu.setBorn(resultSet.getInt("born"));
                    return stu;
                }
            }, 1004)
        }
        
        • 获取多个
        List<T> query(String sql, RowMapper<T> mapper)
        List<T> query(String sql, Object[] args, RowMapper<T> mapper)
        List<T> query(String sql, RowMapper<T> mapper, Object... arg)
        
        public void testQueryEntityList() {
            String sql = "select * from student";
            List<Student> stus = jdbcTemplate.query(sql, new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throw SQLException {
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("id"));
                    stu.setName(resultSet.getInt("name"));
                    stu.setSex(resultSet.getInt("Sex"));
                    stu.setBorn(resultSet.getInt("born"));
                    return stu;
                }
            })
        }
        
        在日常开发中, 匿名类的形式比较冗余, 一般进行封装, 将RowMapper单独实现
        private class StudentRowMapper implements RowMapper<Student> {
                public Student mapRow(ResultSet resultSet, int i) throw SQLException {
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("id"));
                    stu.setName(resultSet.getInt("name"));
                    stu.setSex(resultSet.getInt("Sex"));
                    stu.setBorn(resultSet.getInt("born"));
                    return stu;
                }
        }
        
        使用如下:
        public void testQueryEntityList() {
            String sql = "select * from student";
            List<Student> stus = jdbcTemplate.query(sql, new StudentRowMapper());
        
        但是在实际开发中, 每个类都要实现一个RowMapper会让开发变得极其繁琐, 一般实现一个通用的工具类来做转换的功能:
    • JDBCTemplate优缺点

      • 优点
        • 简单
        • 灵活
      • 缺点
        • sql和java代码牵扯的比较杂乱
        • 功能不够丰富, 比如分页的功能没有等

    Spring事务管理

    什么是事务

    • 事务就是正确执行一系列的操作(或者动作), 使数据库从一种状态转换成另一种状态且保证操作全部成功, 或者全部失败

    事务的特点

    • 原子性(Atomicity)
      • 即不可分割性, 事务要么全部被执行, 要么就全部不执行
    • 一致性(Consistency)
      • 事务的执行使得数据库从一种正确的状态转换成另一种正确的状态
    • 隔离性(Isolation)
      • 在事务正确提交之前, 它可能的结果不应该显示给任何其他事务
    • 持久性(Durability)
      • 事务正确提交以后, 其结果将永久保存在数据库中

    Java事务的产生

    • 程序操作数据库的需要. 在Java编写的程序或者系统中, 实现ACID的操作
    • Java事务实现范围
      • 通过jdbc相应方法间接来实现对数据库的增删改查, 把事务转移到Java程序代码中进行控制
      • 确保事务, 要么全部执行成功, 要么撤销不执行
    • Java事务机制和原理就是确保数据库操作的ACID特性

    Java事务实现模式

    • Java事务的实现
      • 通过Java代码来实现对数据库的事务性操作
    • Java事务类型
      • JDBC事务: 用Connection对象控制, 包括手动模式和自动模式;
      • JTA(Java Transaction API)事务: 与实现无关, 与协议无关的API;
      • 容器事务: 应用服务器提供的, 且大多是基于JTA完成(通常基于JNDI的, 相当复杂的API实现)

    三种事务的差异

    • JDBC事务: 控制的局限性在一个数据库连接内, 但是其使用简单
    • JTA事务: 功能强大, 可跨越多个数据库或者多个DAO, 使用比较复杂
    • 容器事务: 主要指的是J2EE应用服务器提供的事务管理, 局限于EJB

    事务接口

    • 事务接口架构


      事务接口架构

    事务读取类型

    - 脏读: 事务没提交, 提前读取
    - 不可重复读: 多次读取到的数据不一致
    - 幻读: 事务不是独立执行时发生的一种非预期现象
    

    事务隔离级别

    隔离级别

    事务传播行为

    事务传播行为

    事务超时

    • 事务超时就是事务的一个定时器, 在特定时间内事务如果没有执行完毕, 那么久会自动回滚, 而不是一直等待其结束

    事务回滚

    • 默认情况下, 事务只有遇到运行期异常才会回滚, 而在遇到检查型异常时不会回滚
    • 自定义回滚策略
      • 生命失误在遇到特定的检查型异常时像遇到运行期异常那样回滚
      • 声明事务遇到特别的异常不回滚, 即使这些异常时运行期异常

    Spring事务状态

    • 事务接口
      • 通过事务管理器获取TransactionStatus实例;
      • 控制事务在回滚或提交的时候需要应用对应的事务状态;
      • Spring事务接口
      // Spring事务状态接口
      // 通过调用PlatformTransactionManager的getTransaction()
      // 获取事务状态实例
      public interface TransactionStatus {
          boolean isNewTransaction(); // 是否是新的事务
          boolean hasSavepoint(); // 是否有恢复点
          boolean setRollbackOnly(); // 设置为只回滚
          boolean isRollbackOnly(); // 是否为只回滚
          boolean isCompleted(); // 是否已完成
      }
      

    编程式事务实现方式

    事务管理器(PlatformTransactionManager)方式

    • 类似应用JTA UserTransaction API方式, 但异常处理更简洁;
    • 核心类为: Spring事务管理的三个接口类以及JdbcTemplate
    • 步骤:
      • 获取事务管理器
      • 创建事务属性对象
      • 获取事务状态对象
      • 创建JDBC模板对象
      • 业务数据操作处理

    模板事务(TransactionTemplate)的方式

    • 此为Spring官方团队推荐的编程式事务管理方式
    • 主要工具为JdbcTemplate

    声明式事务管理

    声明式事务实现原理

    • 基于AOP模式机制, 对方法前后进行拦截

    声明式事务管理的配置类型:

    • 5种类型: 独立代理; 共享代理, 拦截器, tx拦截器, 全注释(注解方式) (前三类2.0版本以后不推荐使用)

    声明式事务管理配置实现方式:

      <!--    引入配置文件-->
      <bean id="propertyConfigurer"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="order" value="2"/>
          <property name="ignoreUnresolvablePlaceholders" value="true"/>
          <property name="locations">
              <list>
                  <!--配置属性文件-->
                  <value>classpath:datasource.properties</value>
              </list>
          </property>
          <property name="fileEncoding" value="utf-8"/>
      </bean>
    
      <!--配置连接池-->
      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
          <property name="driverClassName" value="${db.driverClassName}"/>
          <property name="url" value="${db.url}"/>
          <property name="username" value="${db.username}"/>
          <property name="password" value="${db.password}"/>
          <!-- 连接池启动时的初始值 -->
          <property name="initialSize" value="${db.initialSize}"/>
          <!-- 连接池的最大值 -->
          <property name="maxActive" value="${db.maxActive}"/>
          <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
          <property name="maxIdle" value="${db.maxIdle}"/>
          <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
          <property name="minIdle" value="${db.minIdle}"/>
          <!-- 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制 -->
          <property name="maxWait" value="${db.maxWait}"/>
          <!--#给出一条简单的sql语句进行验证 -->
          <!--<property name="validationQuery" value="select getdate()" />-->
          <property name="defaultAutoCommit" value="${db.defaultAutoCommit}"/>
          <!-- 回收被遗弃的(一般是忘了释放的)数据库连接到连接池中 -->
          <!--<property name="removeAbandoned" value="true" />-->
          <!-- 数据库连接过多长时间不用将被视为被遗弃而收回连接池中 -->
          <!--<property name="removeAbandonedTimeout" value="120" />-->
          <!-- #连接的超时时间,默认为半小时。 -->
          <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/>
    
          <!--# 失效检查线程运行时间间隔,要小于MySQL默认-->
          <property name="timeBetweenEvictionRunsMillis" value="40000"/>
          <!--# 检查连接是否有效-->
          <property name="testWhileIdle" value="true"/>
          <!--# 检查连接有效性的SQL语句-->
          <property name="validationQuery" value="SELECT 1 FROM dual"/>
      </bean>
      <!-- 使用@Transactional进行声明式事务管理需要声明下面这行 -->
      <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
      <!-- 事务管理 -->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <property name="dataSource" ref="dataSource"/>
          <property name="rollbackOnCommitFailure" value="true"/>
      </bean>
    

    相关文章

      网友评论

          本文标题:Spring

          本文链接:https://www.haomeiwen.com/subject/rogijctx.html