zoukankan      html  css  js  c++  java
  • Spring使用

    一、核心基础

    (一)基于XML的使用

      1、Ioc配置

        (1)Bean标签介绍

          bean标签作用:用于配置被Spring容器管理的bean信息。默认情况下它调用的是类中的无参构造函数,如果没有无参构造,则不能创建。

          bean标签属性:

    标签 描述
    id 给对象在容器中提供一个唯一的标识,用于获取对象。
    class 指定类的全限定名。用于反射创建对象。默认下调用无参构造
    init-method 指定类中的初始化方法名称
    destory-method 指定类中的销毁方法,比如DataSource的配置中,一般需要配置destory-method="close"
    scope

    指定对象的作用范围:

      (1)singleton:默认值,单例,生命周期:

          a、创建:当应用加载,创建容器时,对象就被创建

          b、存活:只要容器存在,一直存货

          c、死亡:当应用卸载,容器销毁时,对象就被销毁

      (2)prototype:多例,每次访问对象时,都会重新创建对象实例。生命周期如下:

          a、创建:访问对象时

          b、存活:只要对象还在使用

          c、死亡:当对象长时间不使用,被垃圾回收器回收

      (3)request:将Spring创建的Bean对象存入到request中

      (4)session:将Spring创建的Bean对象存入Session中

      (5)global session:全局Session

        (2)Bean标签的实例化方式

        a、使用默认无参构造函数(推荐)

        在默认情况下,他会根据默认的无参构造函数来创建对象,如果不存在无参对象,则创建失败。

    <?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="userService" class="com.lcl.galaxy.spring.service.impl.UserServiceImpl"/>
    </beans>

        b、使用静态工厂创建

        使用工厂类的静态方法创建一个对象,并将对象放入Spring容器中

    package com.lcl.galaxy.spring.factory;
    
    import com.lcl.galaxy.spring.service.UserService;
    import com.lcl.galaxy.spring.service.impl.UserServiceImpl;
    
    public class StaticFactory {
        public static UserService createUserService(){
            return new UserServiceImpl();
        }
    }
        <bean id="userService2" class="com.lcl.galaxy.spring.factory.StaticFactory" factory-method="createUserService"/>

        c、实例工厂(了解)

        先将工厂类放入Spring容器中,然后再引用工厂Bean

    package com.lcl.galaxy.spring.factory;
    
    import com.lcl.galaxy.spring.service.UserService;
    import com.lcl.galaxy.spring.service.impl.UserServiceImpl;
    
    public class InstanceFactory {
        public UserService createUserService(){
            return new UserServiceImpl();
        }
    }
        <bean id="instanceFactory" class="com.lcl.galaxy.spring.factory.InstanceFactory"/>
        <bean id="userService3" factory-bean="instanceFactory" factory-method="createUserService"/>

      2、DI配置

        DI是指Bean中的属性,依赖(属性)分为简单类型(String和8种基本类型)、对象类型、集合类型;

        依赖注入是Spring IoC的具体实现;

        (1)依赖注入

        为什么要依赖注入:因为Bean对象的创建我们都交给了Spring创建,那么Bean对象种的指,也肯定是需要交给Spring来赋值的。

        依赖注入的方式有两种:构造函数注入和set方法注入

        a、构造函数注入

        构造函数注入就是使用类的构造函数,给成员变量赋值,赋值是Spring直接进行的赋值。

    package com.lcl.galaxy.spring.service.impl;
    
    import com.lcl.galaxy.spring.domain.UserDo;
    import com.lcl.galaxy.spring.service.UserService;
    
    public class UserServiceImpl implements UserService {
    
        private String id;
        private String name;
    
        @Override
        public UserDo getUserById() {
            return UserDo.builder().id(this.id).name(this.name).build();
        }
    
        public UserServiceImpl(){
            this.id = "initId";
            this.name = "initName";
        }
    
        public UserServiceImpl(String id, String name){
            this.id = id;
            this.name = name;
        }
    
    }
        <bean id="userService4" class="com.lcl.galaxy.spring.service.impl.UserServiceImpl">
            <constructor-arg name="id" value="1"/>
            <constructor-arg name="name" value="lcl"/>
        </bean>

        从上面可以看到,给Service传值,类中必须要有构造函数,同时在bean标签中还需要设置constructor-arg标签,在constructor-arg标签中,有以下几个属性:

            index:指定参数在构造函数中的索引位置

            name:指定参数在构造函数中的名称

            value:赋值操作,可以对简单类型赋值(简单类型:8中基本类型+String)

            ref:赋值操作,可以配置在spring中已经配置的bean

        b、set方法注入

          set方法注入有手动的装配方式(Xml方式)和自动装配方式(注解方式:会用到@Autowired、@Resource、@Inject这些注解)

          手动的装配方式,需要设置bean标签的property标签,同时需要在bean对象中有setter方法。

        <bean id="userService5" class="com.lcl.galaxy.spring.service.impl.UserServiceImpl">
            <property name="id" value="1"/>
            <property name="name" value="lcl"/>
        </bean>

        (2)依赖注入不同类型

             a、简单类型

        <bean id="userService4" class="com.lcl.galaxy.spring.service.impl.UserServiceImpl">
            <constructor-arg name="id" value="1"/>
            <constructor-arg name="name" value="lcl"/>
        </bean>

          b、引用类型

        <bean id="userDao" class="com.lcl.galaxy.spring.dao.UserDao"/>
        <bean id="userService6" class="com.lcl.galaxy.spring.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"/>
        </bean>

          c、集合类型-List

        <bean id="collectionDto" class="com.lcl.galaxy.spring.dto.CollectionDto">
            <property name="nameList">
                <list>
                    <value>lcl</value>
                    <value>qmm</value>
                </list>
            </property>
        </bean>

          d、集合类型-Set

        <bean id="collectionDto2" class="com.lcl.galaxy.spring.dto.CollectionDto">
            <property name="nameList">
                <set>
                    <value>lcl</value>
                    <value>qmm</value>
                </set>
            </property>
        </bean>

          e、集合类型-Map

        <bean id="collectionDto3" class="com.lcl.galaxy.spring.dto.CollectionDto">
            <property name="nameMap">
                <map>
                    <entry key="lcl" value="18"/>
                    <entry key="qmm" value="15"/>
                </map>
            </property>
        </bean>

          f、集合类型-properties

        <bean id="collectionDto4" class="com.lcl.galaxy.spring.dto.CollectionDto">
            <property name="properties">
                <props>
                    <prop key="lcl">21</prop>
                    <prop key="qmm">18</prop>
                </props>
            </property>
        </bean>

    (二)基于注解和XML的混合使用

        这里使用注解和XML混合使用,主要是指的在XML文件中设置自动扫描,而在具体的bean中使用@Service等注解以便spring可以扫描到。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    
        <context:component-scan base-package="com.lcl.galaxy.spring"/>
    </beans>
    package com.lcl.galaxy.spring.service.impl;
    
    import com.lcl.galaxy.spring.service.UserService2;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    @Service
    @Slf4j
    public class UserServiceImpl2 implements UserService2 {
    
        public UserServiceImpl2(){
          log.info("无参构造被调用");
        }
    }

        那么具体有哪些注解可以被扫描到,清单如下所示:

    分类 注解 作用  属性

    Ioc注解

    相当于:

     <bean id="" class=""/>
       
     @Component  让spring来管理资源,相当于对xml中配置的一个bean 指定bean的id,如果不指定,默认bean的id是当前类名,首字母小写 
     @Controller 对于@Component的衍生注解,一般用于表现层注解   
     @Service 同上,主要用于业务层注解   
     @Repository 同上,主要用于持久层注解   

     DI注解

    依赖注入

    相当于:

    <property name="" value=""/>
    <property name="" ref=""/>
      
     @Autowired

     1、默认按照类型装配

    2、是由org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor实现的

    3、是spring自带的注解

    4、@Autowired注解默认情况下要求依赖对象必须存在,如果允许不存在,需要设置它的required属性为false

    5、如果我们想按照名称装配(byName),可以结合@Qualifier注解进行使用

     
     @Qualifier

    1、在自动按照类型注入的同时,在按照bean的id注入

    2、它在给字段注入时不能独立使用,必须和@Autowired一起使用

    3、给方法参数注入时,可以单独使用 

     
     @Resource

    1、默认按照名称装配,可以通过@Resource的name属性指定名称,如果没有指定name属性,当注解写到字段上时,默认取字段名并按照字段名查找,当找不到与字段名匹配的bean时才按照类型进行装配

    2、如果name属性指定,则一定会按照名称进行装配

     
    @Inject

    1、根据类型自动装配,如果需要按照名称进行装配,则需要配合@Name注解

    2、@Inject可以作用在变量、setter方法、构造函数上

     
    @Value 给简单类型(8种基本类型+String)来注入值  

    @Autowired、@Resoure、@Inject区别

    1、@Autowired是spring自带的,@Inject是JSR330规范实现的,@Resource是JSR250规范实现的,需要导入不同的包

    2、@Autowired和@Inject用发基本一致,但是@Autowired有一个request属性

    3、@Autowired和@Inject默认按照类型匹配,@Resource默认按照名称匹配

    4、@Autowired要是想按照名称匹配,需要@Qualifier注解配合;@Inject要是想按照名称匹配,需要@Name注解配合

     

    @Scope

    作用域注解:

    改变作用域,相当于下面的配置代码,value内容有:singletion、prototype、request、session、globalsession

    <bean id="" class="" scope=""/>
     

     @PostConstrust

    @PreDestory

     生命周期注解:

    相当于以下代码

       <bean id="" class="" init-method="" destroy-method=""/>
     

        xml配置和注解配置的对比

      Xml配置 注解配置
    Bean定义 使用<bean id="" class=""/>来配置 使用@Component和其衍生注解@Controller、@Service、@Repository
    Bean名称 通过id或name来设置 通过注解内加名称使用,例如:@Service("UserService")
    Bean注入

    使用<property>标签注入

    使用@Autowired或@Inject或@Inject来注入

    如果使用@Autowired注入,可以配合@Qualifier按名称注入等

    生命过程和作用域 在bean标签中使用init-method、destory-method、scope来设置 使用@PostConstruct、@PreDestory、@Scope来设置
    适用场景

    Bean来自第三方

    Bean是由我们自己写的

    (三)基于纯注解的使用

        1、@Configuration

          @Configuration是用来配置Bean的,对应的就是原来spring的xml配置文件,同样,在主函数中也是需要加载该配置类的

    package com.lcl.galaxy.spring.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @Slf4j
    public class SpringConfiguration {
    
        public SpringConfiguration(){
            log.info("spring容器启动");
        }
    }
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        对应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"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    
    </beans>
    ApplicationContext factory2=new ClassPathXmlApplicationContext("classpath:spring-config2.xml");

       2、@Bean

        Bean标签用来注册bean

        @Bean
        @Scope("singletion")
        public DefaultSqlSessionFactory getSqlSession(){
            return new DefaultSqlSessionFactory(new org.apache.ibatis.session.Configuration());
        }

        对应XML编写

    <bean id="userDao" class="com.lcl.galaxy.spring.dao.UserDao"/>

      3、@CompenentScan

        指定要扫描的包

    @Configuration
    @Slf4j
    @ComponentScan(basePackages = "com.lcl.galaxy.spring")
    public class SpringConfiguration {
    
        public SpringConfiguration(){
            log.info("spring容器启动");
        }
    
    }

        对应xml代码

        <context:component-scan base-package="com.lcl.galaxy.spring"/>

      4、@PropertySource

      加载properties文件的内容

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://******
    jdbc.username=******
    jdbc.password=******
    package com.lcl.galaxy.spring.config;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    
    import javax.sql.DataSource;
    
    @Configuration
    @PropertySource("classpath:db.properties")
    @Slf4j
    public class JdbcConfig {
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        @Bean(name = "dataSource")
        public DataSource createDataSource() throws Exception{
            DataSource dataSource = new ComboPooledDataSource();
            ((ComboPooledDataSource) dataSource).setDriverClass(driver);
            ((ComboPooledDataSource) dataSource).setJdbcUrl(url);
            ((ComboPooledDataSource) dataSource).setUser(username);
            ((ComboPooledDataSource) dataSource).setPassword(password);
            log.info("dabasource=============【{}】", dataSource);
            return dataSource;
        }
    }

      对应XML代码

        <context:property-placeholder location="classpath:db.properties"/>
        <bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>

      5、@Import

        导入其他Spring配置类

    @Configuration
    @Slf4j
    @ComponentScan(basePackages = "com.lcl.galaxy.spring")
    @Import(JdbcConfig.class)
    public class SpringConfiguration {
    }

        Xml代码

    <import resource="spring-config1.xml"/>

    二、核心高级

    (一)AOP

      1、相关术语

    术语 说明  
    JoinPoint 连接点:指的是那些被拦截的点。在spring中,连接点指的是方法,因为spring只支持方法类型的连接点  
    PointCut 切入点:指我们要对哪些JointPoint进行拦截  
    Advice 通知/增强:指拦截到JointPoint后要做的事。通知分为前置通知、后置通知、异常通知、最终通知和环绕通知  
    Introduction 引介:是一种特殊的通知,在不修改代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field  
    Target

    目标对象:代理的目标对象

     
    Weaving 织入:是吧增强应用到目标对象来创建新的代理对象的过程  
    Proxy 代理:一个类被AOP织入增强后,就产生一个代理类  
    Aspect 切面:是切点和通知的结合  
    Advisor 通知器/顾问:和Aspect相似  

      2、织入过程

        对于AOP的织入过程,可以分为动态织入和静态织入。

        其中,动态织入实在运行时动态的将要增强的代码织入到目标类中,这种一般基于动态代理来完成,例如JDK的动态代理(Proxy,通过反射实现)或者CGLIB动态代理(通过继承实现),其中,Spring Aop采用的就是基于运行时增强的动态代理技术

      (1)静态代理:AspectJ

        静态代理是在程序进行编译的时候进行的织入,这就需要一种特殊的程序编译器,例如acj编译器,他主要是先将增强的源代码编译成字节码文件(class文件),然后在编译目标对象时,将增强的字节码文件一起织入,生成最终增强后的字节码文件。

      (2)动态代理

        spring AOP是通过动态代理技术实现的,而动态代理又是通过反射实现的。动态代理有两种实现方式:基于接口的JDK动态代理和基于接口的CGLIB动态代理。

        无论是基于什么的动态代理,都是在运行期,针对目标对象胜场Proxy代理对象,然后在代理对象中织入增强处理。

       a、JDK动态代理

        public static UserService getProxyByJdk(final UserService userService){
            //使用proxy类生成代理对象
            UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                    userService.getClass().getInterfaces(),
                    new InvocationHandler() {
                        //代理对象方法一执行,invoke方法就会执行一次
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if("save".equals(method.getName())){
                                log.info("=============记录日志==============");
                            }
                            //让service类的save方法正常的执行下去
                            return method.invoke(userService, args);
                        }
                    });
            //返回代理对象
            return proxy;
        }

        b、CGLIB

        public static UserService getProxyByCglib(){
            //创建CGLIB核心类
            Enhancer enhancer = new Enhancer();
            //设置父类
            enhancer.setSuperclass(UserServiceImpl.class);
            //设置回调函数
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    if("save".equals(method.getName())){
                        log.info("=============记录日志==============");
                        log.info("=============开启事务==============");
                    }
                    log.info("=============提交事务志==============");
                    return methodProxy.invokeSuper(o, objects)
                }
            });
            //生成代理对象
            UserService proxy = (UserService) enhancer.create();
            return proxy;
        }

    (二)基于AspectJ的AOP使用

         基于AspectJ的AOP使用,其实就是Spring对于AspectJ的整合,不过Spring已经将AspectJ整合到自身的框架种了,并且底层织入仍然采用的是动态织入的方式。

        1、准备代码

      (1)添加依赖

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.0.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>aopalliance</groupId>
                <artifactId>aopalliance</artifactId>
                <version>1.0</version>
            </dependency>

        (2)编写目标类和目标方法

    package com.lcl.galaxy.spring.aop;
    
    public interface UserService {
        public void insert();
    
        public void insert(String id);
    
        public void insert(String id, String name);
    
        public void userInsert();
    }
    package com.lcl.galaxy.spring.aop.service;
    
    import com.lcl.galaxy.spring.aop.UserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    @Slf4j
    @Service
    public class UserServiceImpl implements UserService {
    
        @Override
        public void insert() {
            log.info("=========================insert======================");
        }
    
        @Override
        public void insert(String id) {
            log.info("=========================insert111======================");
        }
    
        @Override
        public void insert(String id, String name) {
            log.info("=========================insert2222======================");
        }
    
        @Override
        public void userInsert() {
            log.info("=========================userInsert======================");
        }
    }

        2、Xml实现

        (1)编写通知

    package com.lcl.galaxy.spring.aop.advice;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class MyAdvice {
        public void log(){
          log.info("===================打印日志===================");
        }
    }

        (2)配置通知、AOP切面、自动扫描包

    <?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"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <context:component-scan base-package="com.lcl.galaxy.spring.aop"/>
        <bean name="myAdvice" class="com.lcl.galaxy.spring.aop.advice.MyAdvice"/>
        <aop:config>
            <aop:aspect ref="myAdvice">
                <aop:before method="log" pointcut="execution(public void com.lcl.galaxy.spring.aop.service.UserServiceImpl.insert())"/>
            </aop:aspect>
        </aop:config>
    
    </beans>

        切入点表达式:

          execution([修饰符] 返回值类型 包名.类名.方法名(参数))   

        表达式说明:

    参数 说明
    execution 必须
    修饰符 可省略
    返回值类型 必须
    包名

    1、多级包之间使用.分割

    2、包可以使用*代替

    3、多级包名可以使用多个*代替

    4、如果想省略中间的包,可以使用.. 

    类名

    1、可以使用*代替

    2、也可以使用*ServiceImpl

    方法名

    1、可以使用*代替

    2、也可以写成save* 

    参数

    1、参数使用*代替

    2、如果有多个参数,可以使用..代替

        对于不同的写法如下代码所示:

    <aop:config>
            <aop:aspect ref="myAdvice">
                <!-- 详细写法 -->
                <!--   <aop:before method="log" pointcut="execution(public void com.lcl.galaxy.spring.aop.service.UserServiceImpl.insert())"/>  -->
                <!-- 省略execution写法-->
                <!-- <aop:before method="log" pointcut="execution(void com.lcl.galaxy.spring.aop.service.UserServiceImpl.insert())"/> -->
                <!-- 一层包名使用*写法 -->
                <!--<aop:before method="log" pointcut="execution(void com.lcl.galaxy.spring.aop.*.UserServiceImpl.insert())"/>-->
                <!-- 多层包名省略写法-->
                <!-- <aop:before method="log" pointcut="execution(void com.lcl.galaxy..insert())"/>-->
                <!-- 类名使用*写法-->
                <!-- <aop:before method="log" pointcut="execution(void com.lcl.galaxy.spring.aop.service.*Impl.insert())"/>-->
                <!-- 方法名使用*写法-->
                <!-- <aop:before method="log" pointcut="execution(void com.lcl.galaxy.spring.aop.service.*Impl.insert*())"/>-->
                <!-- 方法名使用*写法-->
                <!-- <aop:before method="log" pointcut="execution(void com.lcl.galaxy.spring.aop.service.*Impl.insert*())"/>-->
                <!-- 方法名使用*写法-->
                <aop:before method="log" pointcut="execution(void com.lcl.galaxy.spring.aop.service.UserServiceImpl.insert(..))"/>
            </aop:aspect>
        </aop:config>

        通知类型

    通知类型 执行时机 配置文件 应用场景
    前置通知 目标对象方法之前执行通知
    <aop:before method="" pointcut=""/> 
    方法开始时进行校验
    后置通知 目标对象方法之后执行,有异常不执行
    <aop:after-running method="" pointcut=""/>
    可以修改方法的返回值
    最终通知 目标对象方法之后执行,有异常也执行
    <aop:after method="" pointcut=""/>
    资源释放等
    环绕通知 目标对象方法之前和之后都会执行
    <aop:around method="" pointcut=""/>
    事务,统计代码执行时间等
    异常通知 目标对象方法执行异常会执行
    <aop:after-throwing method="" pointcut=""/>
    包装异常

        3、注解 + xml 实现

        (1)通知代码

    package com.lcl.galaxy.spring.aop.advice;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Aspect
    @Component("myAdviceAuto")
    public class MyAdviceAuto {
    
        @Before(value = "execution(void *..*Impl.insert(..))")
        public void log(){
          log.info("===================打印日志===================");
        }
    }

        (2)配置文件

        目标类仍然使用上面的目标类,但是需要在配置文件添加注解扫描和开启AOP自动代理

        <context:component-scan base-package="com.lcl.galaxy.spring.aop"/>
        <aop:aspectj-autoproxy/>

        (3)对于环绕通知的写法

        @Around(value = "execution(* *..*Impl.*(..))")
        public String trans(ProceedingJoinPoint proceedingJoinPoint){
            Object[] args = proceedingJoinPoint.getArgs();
            String proceed = null;
            try {
                proceed = "返回结果" + (String) proceedingJoinPoint.proceed(args) ;
            } catch (Throwable throwable) {
                return "================异常";
            }
            return proceed;
        }

        (4)定义通用切入点

        通用切入点指的是,再通知内设置value时,不再设置具体的拦截方案,会调用一个通用的拦截方案。

        在通用

        @Before(value = "MyAdviceAuto.fn()" )
        public void logfn(){
            log.info("=========================logfn=============================");
        }
    
        @Before(value = "MyAdviceAuto.fn()" )
        public void killfn(){
            log.info("=========================killfn=============================");
        }
    
        @Pointcut(value = "execution(* *..*.*(..))")
        public void fn(){
            log.info("===================fn()====================");
        }

        4、纯注解方式

        纯注解和注解+xml的区别仅在于需要使用注解替换在xml中配置的,直接在配置类上加上@EnableAspectJAutoProxy注解即可,其余的都一致。

        那么 注解+xml 的方式在xml里面主要做了两件事,注解扫描和开启AOP自动代理

    package com.lcl.galaxy.spring.demo.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
    import org.springframework.context.annotation.*;
    
    @Configuration
    @Slf4j
    @ComponentScan(basePackages = "com.lcl.galaxy.spring")
    @Import(JdbcConfig.class)
    @EnableAspectJAutoProxy
    public class SpringConfiguration {
    
        public SpringConfiguration(){
            log.info("spring容器启动");
        }
    
        @Bean
        @Scope("singletion")
        public DefaultSqlSessionFactory getSqlSession(){
            return new DefaultSqlSessionFactory(new org.apache.ibatis.session.Configuration());
        }
    
    }

    三、组件支撑

    (一)整合Junit

        在测试类中,每一个测试类都要有加载xml文件的代码

    @Slf4j
    public class LclSpringTest {
    
        @Test
        public void test1(){
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-config4.xml");
            UserService userService = (UserService) context.getBean("userServiceImpl");
            String name = userService.around("lcl");
            log.info("=============={}", name);
        }
    }

        可以看到上面的测试代码,在测试方法中需要加载spring配置文件,同时需要获取对应的Bean,但是如果有很多测试方法,那么每个方法都需要去写这两行代码。

        好的一点是,Junit提供了RunWith注解用来让我们替换他的执行器,同时,Spring也提供了一个运行器,可以读取配置文件来创建容器,我们只需要告诉它配置文件的地址就OK。

        那么主要就需要调整三个点:

          (1)使用Junit的@RunWith注解,向注解内传入Spring的Junit执行器

          (2)使用@ContextConfiguration注解加载配置文件或配置类

          (3)使用@Autowired注解直接获取Bean

    package com.lcl.galaxy.mybatis;
    
    import com.lcl.galaxy.spring.aop.UserService;
    import com.lcl.galaxy.spring.demo.config.SpringConfiguration;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @Slf4j
    @RunWith(SpringJUnit4ClassRunner.class)
    //@ContextConfiguration(locations = "classpath:spring-config4.xml")
    @ContextConfiguration(classes = SpringConfiguration.class)
    public class LclSpringTest2 {
    
        @Autowired
        private UserService userService;
    
        @Test
        public void test(){
            String name = userService.around("lcl");
            log.info("=============={}", name);
        }
    }

    (二)事务支持

      1、事务相关特性介绍

      (1)事务的特性(ACID)

    事务缩写 名称 说明
    A 原子性:Atomicty 事务是一个原子操作,只会成功或失败,不存在中间状态
    C 一致性:Consistency 在一个事务内,要么全部成功,要么全部失败
    I 隔离性:Isolation 各个事务相互隔离,一个事务没有提交之前,不会被其他事务看到
    D 持久性:Durability 指一个事务一旦被提交,就是永久性的。

      (2)事务的并发问题

    问题 描述
    脏读 一个事务读取到了另一个事务未提交的数据
    不可重复读 一个事务因读取到了另一个事务已提交的数据,导致对同一条记录读取两次以上的结果不一致,主要针对update
    幻读 一个事务因读取到了另一个事务已提交的数据,导致对同一条记录读取两次以上的结果不一致,主要针对delete和insert

      (3)事务隔离级别

        四种隔离级别

    级别 描述
    读未提交:Read uncommitted 最低级别,任何情况都无法保证
    读已提交:Read committed 可以避免脏读
    可重复读:Repeatable Read 可以避免脏读、不可重复读
    串行化:Serializable 可以避免脏读、不可重复读、幻读

        大多数数据库的默认隔离级别是ReadCommitted,例如Oracle、DB2等

        MySql的默认隔离级别是可重复读(Repeatable Read)

        这里有一点需要注意:隔离级别越高,越能保证数据的完整性和一致性,但是对于并发性能影响也越大

      (4)Spring事务管理接口

        Spring并不是直接操作事务,而是提供事务管理接口PlatformTransactionManager,通过这个接口,Spring为各个平台(JDBC、Hibernate等)都提供了对应的事务管理器。

        这里有几个接口需要介绍一下

    接口名称 说明 实现类 常用方法
    PlatformTransactionManager接口 平台事务管理器,是真正管理事务的类,该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类。

    JDBCMybatis:DataSourceTransactionManager

    Hibernate:HibernateTransactionManager

    获取事务状态方法: TransactionStatus getTransaction(TransactionDefinition definition)

    提交事务方法:void commit(TransactionStatus status)

    回滚事务方法:void rollback(TransactionStatus status)

    TransactionDefinition接口 事务定义信息,可以定义事务隔离级别、事务传播行为、超时时间、是否只读等信息    
    TransactionStatus接口 事务的状态:是否是新事务、是否已提交、是否有保存点、是否回滚    

        上述几个接口的关系:平台事务管理器(PlatformTransactionManager)真正管理事务对象,根据事务定义信息(TransactionDefination)进行事务管理,在管理事务中产生一些状态,将状态等信息记录在事务状态(TransactionStatus)中。

        对于TransactionDefinition中对于事务隔离级别和传播行为有以下几种定义

        a、事务隔离级别常量

    隔离级别常量 隔离级别描述
    ISOLATION_DEFAULT 采用数据库默认的隔离级别
    ISOLATION_READ_UNCOMMITTED 读未提交
    ISOLATION_READ_COMMITTED 读已提交
    ISOLATION_REPEATABLE_READ 可重复读
    ISOLATION_SERIALIZABLE 串行化

        b、事务传播行为(默认值为PROPAGATION_REQUIRED)

    传播行为 描述
    PROPAGATION_REQUIRED A中有事务,使用A中的事务;A中没有事务,B开启一个新的事务
    PROPAGATION_SUPPORTS A中有事务,使用A中的事务;A中没有事务,B也不使用事务
    PROPAGATION_MANDATORY A中有事务,使用A中的事务;A中没有事务,抛出异常
    PROPAGATION_REQUIRED_NEW A中有事务,将A的事务挂起,B创建一个新的事务
    PROPAGATION_NOT_SUPPORTED A中有事务,将A中的事务挂起
    PROPAGATION_NEVER A中有事务,抛出异常
    PROPAGATION_NESTED 嵌套事务,当A执行后,就会在这个位置设置一个保存点,如果B没有问题,执行通过。如果B出现异常,根据需求回滚(回滚到保存点或者最初始状态)

      2、Spring框架事务管理的分类

      主要分为编程式事务管理和声明式事务管理,且声明式事务管理可以使用xml编写方式、xml+注解编写方式和纯注解使用方式。

      (1)编程式事务管理

        编程式事务管理主要是使用了TransactionTemplate模板类,通过该模板类进行事务处理。

        首先编写业务逻辑层代码,在业务逻辑层代码中,调用TransactionTemplate的execute方法,在该方法内的所有操作,都是一个事务。

    package com.lcl.galaxy.spring.transaction.service.impl;
    
    import com.lcl.galaxy.spring.transaction.dao.OrderInfoDao;
    import com.lcl.galaxy.spring.transaction.dao.UserDao;
    import com.lcl.galaxy.spring.transaction.domain.OrderInfoDo;
    import com.lcl.galaxy.spring.transaction.service.UserService;
    import com.lcl.galaxy.spring.transaction.domain.UserDo;
    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.support.TransactionCallbackWithoutResult;
    import org.springframework.transaction.support.TransactionTemplate;
    
    @Slf4j
    @Data
    public class UserServiceImpl implements UserService {
    
    
        @Autowired
        private TransactionTemplate transactionTemplate;
    
        @Autowired
        private UserDao userDao;
        @Autowired
        private OrderInfoDao orderInfoDao;
    
    
        @Override
        public void transactionTest(UserDo userDo, OrderInfoDo orderInfoDo) {
            log.info("事务处理");
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    log.info("=======================");
                    userDao.insert(userDo);
                    orderInfoDao.insert(orderInfoDo);
                }
            });
            log.info("事务结束");
        }
    }

      然后是对TransactionTemplate的配置

    <?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"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="****"></property>
            <property name="user" value="******"></property>
            <property name="password" value="5H5eLQsp6yO4"></property>
        </bean>
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager"/>
        </bean>
    
        <bean id="userDao" class="com.lcl.galaxy.spring.transaction.dao.impl.UserDaoImpl"/>
        <bean id="orderInfoDao" class="com.lcl.galaxy.spring.transaction.dao.impl.OrderInfoDaoImpl"/>
    
        <bean id="userService" class="com.lcl.galaxy.spring.transaction.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"/>
            <property name="orderInfoDao" ref="orderInfoDao"/>
            <property name="transactionTemplate" ref="transactionTemplate"/>
        </bean>
    
    </beans>

        (2)声明式事务--xml实现方式

        使用xml的方式实现声明式事务,主要需要做的就是使用AOP的配置,将事务增强到切面方法上。

        <aop:config>
            <aop:advisor advice-ref="txadvice" pointcut="execution(* *..*.save(..))"/>
        </aop:config>

        对于事务增强,又需要事务管理器来处理

        <tx:advice id="txadvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="save*"/>
                <tx:method name="find*" read-only="true"/>
            </tx:attributes>
        </tx:advice>

        事务管理器又需要DataSource数据源

        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="*****"></property>
            <property name="user" value="******"></property>
            <property name="password" value="******"></property>
        </bean>
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>

        配置Service类

        <bean id="userService2" class="com.lcl.galaxy.spring.transaction.service.impl.UserService2Impl"/>

        最后测试代码

    package com.lcl.galaxy.spring;
    
    @Slf4j
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-transaction-xml-config.xml")
    public class LclSpringTransactionTest {
    
    
        @Autowired
        private UserService2 userService2;
    
        @Test
        public void test2(){
            UserDo userDo = UserDo.builder().address("beijing").username("lcl111").sex("").build();
            OrderInfoDo orderInfoDo = OrderInfoDo.builder().orderId("1111").name("lcl").payMoney(new BigDecimal(String.valueOf("1.01"))).build();
            log.info("测试启动");
            userService2.saveInfo(userDo, orderInfoDo);
            log.info("测试完成");
        }
    }

        (3)声明式事务--注解 + xml实现方式

        使用注解+xml实现声明式事务和纯xml实现声明式事务的主要差别就是我们不需要在xml里面配置事务增强和AOP配置,以及Bean的配置,但是仍然要配置dataSource和TransactionManager。同时需要配置 tx:annotation-driven 标签来开启事务注解

        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="******"></property>
            <property name="user" value="******"></property>
            <property name="password" value="******"></property>
        </bean>
    
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
        <context:component-scan base-package="com.lcl.galaxy.spring.transaction"/>

        在使用事务时,使用@Transaction注解来处理,该注解可以使用在类上也可以使用在方法上

    package com.lcl.galaxy.spring.transaction.service.impl;
    
    
    @Slf4j
    @Data
    @Service
    public class UserService3Impl implements UserService3 {
    
        @Transactional
        @Override
        public void saveInfo(UserDo userDo, OrderInfoDo orderInfoDo) {
            log.info("==================");
        }
    }

        (4)声明式事务--纯注解实现方式

        纯注解的实现方式,在xml中只需要配置DataSource,然后在主类上使用@EnableTransactionManagement注解即可。

    @Slf4j
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:spring-transaction-xml-auto2-config.xml")
    @EnableTransactionManagement
    public class LclSpringAutoTransactionTest2 {
    
        @Autowired
        private UserService3 userService3;
    
        @Test
        public void test(){
            UserDo userDo = UserDo.builder().address("beijing").username("lcl111").sex("").build();
            OrderInfoDo orderInfoDo = OrderInfoDo.builder().orderId("1111").name("lcl").payMoney(new BigDecimal(String.valueOf("1.01"))).build();
            log.info("测试启动");
            userService3.saveInfo(userDo, orderInfoDo);
            log.info("测试完成");
        }
    }
  • 相关阅读:
    面试基础02
    CustomerView
    Java封装性
    Java中参数传递机制:值传递
    Java重载 (Overlaod)与几个面试题
    数据结构、算法概述和简单的JVM内存解析
    异常捕捉

    面向接口的编程方式
    看程序的方法
  • 原文地址:https://www.cnblogs.com/liconglong/p/14100429.html
Copyright © 2011-2022 走看看