Spring详细使用总结

Spring

1 基本使用

pom文件

<dependencies>
    <!--核心依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
</dependencies>

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--导入分支配置文件-->
    <import resource="classpath:spring-school.xml"/>
    <!--自定义对象
		scope:指定作用域
	-->
    <bean id="someService" class="com.beiyin.service.SomeServiceImpl" scope="prototype"/>
    <!--非自定义对象-->
    <bean id="myDate" class="java.util.Date"/>
</beans>

Test

 @Test
    public void test6() {
        // 指定配置文件的位置和名称
        String resource = "applicationContext.xml";
        // String resource = "D:\\applicationContext.xml";
        // 创建spring容器对象
        // 配置文件在根路径下
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        // 配置文件在磁盘路径下
        // ApplicationContext ac = new FileSystemXmlApplicationContext(resource);
        // 从容器对象中获得bean
        SomeService someService = (SomeService) ac.getBean("someService");
        // 执行方法
        someService.doSome();
        Date myDate = (Date) ac.getBean("myDate");
        System.out.println(myDate.toString());
    }

2 Bean的装配

默认调用无参构造器,创建实例对象。

Bean 的作用域

  • singleton 单例模式,默认;
  • prototype 原型模式,每次调用,获得新的实例;
  • request 每次HTTP请求,产生新的实例,只能用于Web应用;
  • session 不同的HTTP session,产生不同的实例,只能用于Web应用。

3 注入/DI

对 bean 对象的属性进行初始化。

3.1 基于XML的DI

设置注入和构造注入

<?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="school" class="com.beiyin.service.School"/>

    <!--设值注入(使用setter/getter方法)-->
    <bean id="student" class="com.beiyin.service.Student">
        <!--基本类型-->
        <property name="name" value="ding"/>
        <property name="age" value="20"/>
        <!--引用类型-->
        <property name="school" ref="school"/>
        <!--使用ref标签-->
        <!--<property name="school">-->
            <!--<ref bean="school"/>-->
        <!--</property>-->
    </bean>

    <!--构造注入-->
    <bean id="myStudent" class="com.beiyin.service.Student">
        <constructor-arg name="name" value="ding"/>
        <constructor-arg name="age" value="14"/>
        <constructor-arg name="school" ref="school"/>
    </bean>

</beans>

引用类型自动注入

byName

<?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="school" class="com.beiyin.service.School"/>
    <!--设值注入(使用setter/getter方法)-->
    <bean id="student" class="com.beiyin.service.Student" autowire="byName">
        <!--基本类型-->
        <property name="name" value="ding"/>
        <property name="age" value="20"/>
    </bean>
</beans>

byType

<?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">
    <!--注意同byName的区别-->
    <bean id="mySchool" class="com.beiyin.service.School"/>
    <!--设值注入(使用setter/getter方法)-->
    <bean id="student" class="com.beiyin.service.Student" autowire="byType">
        <!--基本类型-->
        <property name="name" value="ding"/>
        <property name="age" value="20"/>
    </bean>
</beans>
3.2 基于注解的DI

配置组件扫描器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <import resource="classpath:spring-school.xml"/>
    <!--配置组件扫描器,扫描多个包-->
    <context:component-scan base-package="com.beiyin.service,com.beiyin.dao"/>
</beans>

创建bean

// 指定bean的id
@Component("someService")
public class SomeServiceImpl implements SomeService {
    public SomeServiceImpl(){
        System.out.println("无参构造");
    }

    @Override
    public void doSome() {
        System.out.println("实现doSmoe方法");
    }
}
  • @Component:创建基本的bean;
  • @Repository:创建持久层bean;
  • @Service:创建业务层bean;
  • @Component:创建控制层bean。

基本类型属性注入

public class Student {
    @Value("张三") // 无需setter
    private String name;
}

引用类型属性byType自动注入

@Component("mySchool")
public class School {
}

public class Student {
    @Autowired // 按类型自动注入,无需setter
    private School school;
}

引用类型属性byName自动注入

public class Student {
    @Autowired // 按名称自动注入,无需setter
    @Qualifier("mySchool")
    private School school;
}

required

public class Student {
    @Autowired(required = false) // 当注入失败时,赋予null值
    private School school;
}

@Resource

默认按名称,找不到时再按类型。

public class Student {
    @Resource(name = "mySchool") // 指定名称
    private School school;
}

4 AOP概要

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。

优势 :

  • 减少重复;
  • 专注业务;

AOP术语

  • 切面(Aspect ):泛指交叉业务逻辑,比如事务处理/日志处理。
  • 连接点(JoinPoint ):可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
  • 切入点(Pointcut):指声明的一个或多个连接点的集合。
  • 目标对象(Target ):将要被增强的对象。即包含主业务逻辑的类的对象。
  • 通知(Advice ):通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。

5 AspectJ对AOP的实现

5.1 通知类型
  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知
5.2 切入点表达式

execution ( [modifiers-pattern] 访问权限类型 ret-type-pattern 返回值类型 [declaring-type-pattern] 全限定性类名 name-pattern(param-pattern)方法名(参数类型和参数个数) [throws-pattern] 抛出异常类型 )

符号意义
*0至多个任意字符
方法参数中表示任意多个参数;包名后表示当前包及子包
+用在类/接口名后,表示当前类/接口及其子类/实现类
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为:IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,
如 joke( java.util.List, int)。
execution(* joke(String,*)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型,
如joke(String s1,String s2)和joke(String s1,double d2)都是,
但joke(String s1,double d2,String s3)不是。
execution(* joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
5.3 基于注解的实现

pom.xml

<!--AOP依赖-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>4.3.16.RELEASE</version>
</dependency>

java实现

// 接口
public interface OneService {
    int sayHello(String name,int age);
}

// 实现类
@Component
public class OneServiceImpl implements OneService {
    @Override
    public int sayHello(String name, int age) {
        // 实现抛出异常
        // age = 1/0;
        System.out.println("hello" +name);
        return age + 5;
    }
}

// 切面类
@Component
@Aspect
public class MyAspect {

    /**
     * 前置通知
     * @param joinPoint 代表切入点表达式,可获取切入带你表达式/方法签名/目标对象,所有通知都可包含
     */
    @Before(value = "execution(* com.beiyin.service.aop.OneServiceImpl.*(..))")
    public void before(JoinPoint joinPoint){
        // 连接点的方法定义
        Signature signature = joinPoint.getSignature();
        System.out.println(signature);
        /*
        int com.beiyin.service.aop.OneService.sayHello(String,int)
         */
        // 方法参数信息
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println(arg);
            /*
            丁
			15
             */
        }
        System.out.println("前置通知");
    }

    /**
     *后置通知
     * @param result 定义在注解属性returning中,代表执行结果
     */
    @AfterReturning(value = "execution(* com.beiyin.service.aop.OneServiceImpl.*(..))",returning = "result")
    public void after(Object result){
        // 获得目标方法执行结果
        System.out.println("执行结果:"+result);
        System.out.println("后置通知");
    }

    /**
     * 环绕通知
     * @param pjp 用于执行目标方法
     * @return
     * @throws Throwable
     */
    @Around(value ="execution(* com.beiyin.service.aop.OneServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知:在目标前");
        // 执行目标方法
        Object proceed = pjp.proceed();
        // 修改目标执行结果
        proceed = 5;
        System.out.println("环绕通知:在目标后");
        return proceed;
    }

    /**
     * 异常通知 (注意:只有在切入点的方法发生异常时,才会执行)
     * @param ex 异常对象
     */
    @AfterThrowing(value ="execution(* com.beiyin.service.aop.OneServiceImpl.*(..))",throwing = "ex")
    public void throwing(Throwable ex){
        System.out.println("发生了异常;"+ex.getMessage());
    }

    /**
     * 最终通知
     */
    @After(value = "pt()")
    public void after(){
        System.out.println("无论如何,都会执行");
    }

    /**
     * 定义切入点,简化配置
     */
    @Pointcut(value = "execution(* com.beiyin.service.aop.OneServiceImpl.*(..))")
    private void pt(){

    }
}
/*无异常通知顺序:
环绕通知:在目标前
前置通知
环绕通知:在目标后
最终通知:一定执行
后置通知
*/
/*有异常通知顺序:
环绕通知:在目标前
前置通知
最终通知:一定执行
异常通知
*/

5.4 基于XML的实现

java

// 目标类
public class OneServiceImpl implements OneService {
    @Override
    public int sayHello(String name, int age) {
        // age = 1/0;
        System.out.println("hello" +name);
        return age + 5;
    }
}

// 切面类
public class MyAspect {

    public void before(JoinPoint joinPoint) {
        System.out.println("前置通知");
    }

    public void afterReturn(Object result) {
        System.out.println("后置通知");
    }

    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知:在目标前");
        // 执行目标方法
        Object proceed = pjp.proceed();
        // 修改目标执行结果
        proceed = 5;
        System.out.println("环绕通知:在目标后");
        return proceed;
    }

    public void throwing(Throwable ex) {
        System.out.println("发生了异常;" + ex.getMessage());
    }

    public void after() {
        System.out.println("无论如何,都会执行");
    }

}

配置文件

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建切面对象-->
    <bean id="myAspect" class="com.beiyin.service.aop.MyAspect"/>
    <!--创建目标对象-->
    <bean id="oneServiceImpl" class="com.beiyin.service.aop.OneServiceImpl"/>

    <!--切面配置-->
    <aop:config>
        <!--指定切入点-->
        <aop:pointcut id="pt" expression="execution(* com.beiyin.service.aop.OneServiceImpl.*(..))"/>
        <!--配置通知-->
        <aop:aspect id="ap" ref="myAspect">
            <!--前置通知-->
            <aop:before method="before" pointcut-ref="pt"/>
            <!--后置通知-->
            <aop:after-returning method="afterReturn" pointcut-ref="pt" returning="result"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="pt"/>
            <!--异常通知-->
            <aop:after-throwing method="throwing" pointcut-ref="pt" throwing="ex"/>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>

</beans>

6 Spring实现AOP

java

// 接口
public interface OneService {
    int sayHello(String name,int age);
}

// 实现类
public class OneServiceImpl implements OneService {
    @Override
    public int sayHello(String name, int age) {
        age = 1/0;
        System.out.println("hello" +name);
        return age + 5;
    }
}

/*
切面类
必须实现相应的接口
*/
// 前置通知
public class MyBefore implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
    }
}

// 后置通知
public class MyAfter implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置通知");
    }
}

// 环绕通知
public class MyAround implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕通知:目标前");
        Object proceed = invocation.proceed();
        System.out.println("环绕通知:目标后");
        return proceed;
    }
}

// 异常通知 该接口没有方法,需要自己写方法,名字必须是afterThrowing
public class MyThrow implements ThrowsAdvice {

    /**
     *
     * @param e 测试的是必须只有这一个参数
     */
    public void afterThrowing(Throwable e){
        System.out.println("异常通知");
    }
}

配置文件

<?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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建目标对象-->
    <bean id="oneServiceImpl" class="com.beiyin.service.aop.OneServiceImpl"/>
    <!--创建切面对象-->
    <bean id="before" class="com.beiyin.service.aop.sp.MyBefore"/>
    <bean id="after" class="com.beiyin.service.aop.sp.MyAfter"/>
    <bean id="around" class="com.beiyin.service.aop.sp.MyAround"/>
    <bean id="myThrow" class="com.beiyin.service.aop.sp.MyThrow"/>

    <!--定义代理对象-->
    <bean id="proxyOneService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--目标-->
        <property name="target" ref="oneServiceImpl"/>
        <!--实现的接口-->
        <property name="proxyInterfaces" value="com.beiyin.service.aop.OneService"/>
        <!--指定切面对象-->
        <property name="interceptorNames">
            <list>
                <value>before</value>
                <value>around</value>
                <value>after</value>
                <value>myThrow</value>
            </list>
        </property>
    </bean>

</beans>

7 集成MyBatis

pom.xml

  <dependencies>
      <!--spring核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--spring事务依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--springjdbc依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
      
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <!--spring集成mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.4</version>
        </dependency>
        <!--连接池依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
    </dependencies>

java

// 实体类
@Data
public class User {
    private Integer id;
    private String name;
    private Character sex;
    private Integer age;
    private String address;
}

// dao接口
public interface UserDao {
    List<User> selectAll();
}

// 业务类
public class OneServiceImpl {
    private UserDao userDao;
    public void AllName() {
        List<User> users = userDao.selectAll();
        StringBuilder stringBuilder = new StringBuilder();
        users.forEach(user -> stringBuilder.append(user.getName()));
        System.out.println("所有名字:" + stringBuilder);
    }
    // 为了设置注入
    public OneServiceImpl setUserDao(UserDao userDao) {
        this.userDao = userDao;
        return this;
    }
}

Mapper文件:UserDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.beiyin.dao.UserDao">
    <select id="selectAll" resultType="com.beiyin.entity.User">
        select * from user;
    </select>    
</mapper>

mybatis主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <typeAliases>
        <package name="com.beiyin.entity"></package>
    </typeAliases>
   
    <mappers>
        <package name="com.beiyin.dao"></package>
    </mappers>
</configuration>

数据库配置文件

# 注意mysql8的写法
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
jdbc.name=root
jdbc.pwd = root

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--引入属性配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="clone">
        <!--基本配置-->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.name}"/>
        <property name="password" value="${jdbc.pwd}"/>
        <!--配置初始化大小/最小数/最大数-->
        <property name="initialSize" value="1"/>
        <property name="minIdle" value="1"/>
        <property name="maxActive" value="20"/>
        <!--获取连接最大等待时间-->
        <property name="maxWait" value="60000"/>
        <!--配置检测需要关闭连接时间的间隔-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    </bean>
    <!--注册工厂Bean-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--动态代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.beiyin.dao"/>
    </bean>
    
    <!--业务对象-->
    <bean class="com.beiyin.service.aop.OneServiceImpl" id="oneService">
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>

测试

 @Test
    public void test1(){
        String resource = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        UserDao userDao = (UserDao) ac.getBean("userDao");
        List<User> users = userDao.selectAll();
        users.forEach(user-> System.out.println(user));
        OneServiceImpl oneService = (OneServiceImpl) ac.getBean("oneService");
        oneService.AllName();
    }

8 Spring事务

事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。

8.1 事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。

常用的实现类:

  • DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
  • HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。
8.2 Spring 的回滚方式

Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。

注意:当程序出现运行时异常时,java不要求必须处理(抛出/捕获),因此,当出现运行时异常时,实质上是未处理的状态,或者说是程序员未预料到的状态,此时就需要回滚。

8.3 事务定义接口

事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。

五个事务隔离级别常量

  • DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为READ_COMMITTED。
  • READ_UNCOMMITTED:读未提交。未解决任何并发问题。
  • READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
  • REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读。
  • SERIALIZABLE:串行化。不存在并发问题。

七个事务传播行为常量

  • PROPAGATION_REQUIRED:指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这是默认行为。
  • PROPAGATION_REQUIRES_NEW:指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
  • PROPAGATION_SUPPORTS:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
  • PROPAGATION_MANDATORY
  • PROPAGATION_NESTED
  • PROPAGATION_NEVER
  • PROPAGATION_NOT_SUPPORTED

事务默认超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。比较复杂,一般选择默认。

8.4 相关程序

java

// 实体类
@Data
public class User {
    private Integer id;
    private String name;
    private Character sex;
    private Integer age;
    private String address;
}

// dao接口
public interface UserDao {
    List<User> selectAll();
    int insertByName(User user);
    int deleteByName(String name);

}

// 自定义异常
public class MyException extends RuntimeException {
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

// 业务类
public class UserOptionImpl implements UserOption {
    private UserDao userDao;
    
    @Override
    public void userDo() {
        List<User> users = userDao.selectAll();
        for (User user : users) {
            if ("zhao".equals(user.getName())) {
                int i = userDao.insertByName(user);
                System.out.println("插入记录数;"+i);
            }
        }
        if (1==1){
            // throw new MyException("操作user异常");
        }
        userDao.deleteByName("liu");
    }

    public UserOptionImpl setUserDao(UserDao userDao) {
        this.userDao = userDao;
        return this;
    }
}

// 测试
 @Test
    public void test1(){
        String resource = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        UserOption userOption = (UserOption) ac.getBean("userOption");
        userOption.userDo();
    }
8.5 使用 Spring 的事务注解管理事务

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

@Transactional的可选属性:

  • propagation:事务传播属性,默认值为Propagation.REQUIRED。
  • isolation:事务的隔离级别,默认值为Isolation.DEFAULT。
  • readOnly:设置对数据库只读,默认值为 false。
  • timeout:设置本操作与数据库连接的超时时限,单位为秒,默认-1。
  • rollbackFor:指定需要回滚的异常类,可以是一个,也可以是数组。
  • rollbackForClassName:指定需要回滚的异常类类名。
  • noRollbackFor:指定不需要回滚的异常类。
  • noRollbackForClassName:指定不需要回滚的异常类类名。

注意:@Transactional只能用于public方法上,用于其他方法不会出错,但也无作用。

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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--引入属性配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="clone">
        <!--基本配置-->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.name}"/>
        <property name="password" value="${jdbc.pwd}"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    </bean>
    <!--注册工厂Bean-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--动态代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.beiyin.dao"/>
    </bean>

    <!--定义业务对象-->
    <bean class="com.beiyin.service.UserOptionImpl" id="userOption">
        <property name="userDao" ref="userDao"/>
    </bean>

    <!--声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--声明事务注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

业务方法

public class UserOptionImpl implements UserOption {
    private UserDao userDao;
    @Transactional(rollbackFor = {MyException.class,NullPointerException.class})
    @Override
    public void userDo() {
        List<User> users = userDao.selectAll();
        for (User user : users) {
            if ("zhao".equals(user.getName())) {
                int i = userDao.insertByName(user);
                System.out.println("插入记录数;"+i);
            }
        }
        if (1==1){
            // throw new MyException("操作user异常");
        }
        userDao.deleteByName("liu");
    }
}

8.6 使用 AspectJ 的 AOP 配置管理事务

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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--引入属性配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="clone">
        <!--基本配置-->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.name}"/>
        <property name="password" value="${jdbc.pwd}"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    </bean>
    <!--注册工厂Bean-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--动态代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.beiyin.dao"/>
    </bean>

    <!--定义业务对象-->
    <bean class="com.beiyin.service.UserOptionImpl" id="userOption">
        <property name="userDao" ref="userDao"/>
    </bean>

    <!--声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置事务通知-->
    <tx:advice id="userTransaction" transaction-manager="transactionManager">
        <tx:attributes>
            <!--指定相应方法的事务-->
            <tx:method name="user*" rollback-for="com.beiyin.exception.MyException"/>
            <!--指定除上面个的其他方法的事务-->
            <tx:method name="*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

    <!--aop配置-->
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.beiyin.service.UserOptionImpl.*(..))"/>
        <!--声明增强器-->
        <aop:advisor advice-ref="userTransaction" pointcut-ref="pt"/>
    </aop:config>
</beans>

优势: 无需为每一个目标类添加注解配置,只需在spring中统一定义即可。

9 Spring与Web

pom.xml

 <dependencies>
        <!--spring容器依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--spring-web依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--servlet依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- jsp依赖 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2.1-b03</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

java

// 业务类
public class MyService {

    public void print(){
        System.out.println("---------");
    }
}

// servlet
public class RegServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获得ServletContext容器
        ServletContext context = req.getServletContext();
        // 获取spring容器第一种方法
        WebApplicationContext wc = (WebApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        MyService myService = (MyService) wc.getBean("myService");
        // 获取spring容器第二种方法
        // WebApplicationContext rc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
        // MyService myService = (MyService) rc.getBean("myService");
        myService.print();
        // 获得参数
        String name = req.getParameter("name");
        String age = req.getParameter("age");
        System.out.println("name:"+name+" age:"+age);
        //重定向
        req.getRequestDispatcher("/result.jsp").forward(req,resp);

    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd">

	 <display-name>Archetype Created Web Application</display-name>

	<!--注册servlet-->
	<servlet>
		<servlet-name>regServlet</servlet-name>
		<servlet-class>com.RegServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>regServlet</servlet-name>
		<url-pattern>/reg</url-pattern>
	</servlet-mapping>

	<!--注册监听器-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!--指定spring配置文件-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<!--默认为WEB-INF/applicationContext.xml,但是一般不用-->
		<param-value>classpath:application.xml</param-value>
	</context-param>
</web-app>

index.jsp

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>Hello World!</h2>
<form action="reg" method="post">
    姓名:<input type="text" name="name"/>
    年龄:<input type="text" name="age">
    <input type="submit" value="提交">
</form>
</body>
</html>

10 其他

手动获取spring上下文对象

/**
 * 手动获取Spring上下文和Bean对象
 *
 * @Author YinWenBing
 * @Date 2017/1/6  17:07
 */
@Component
public class ApplicationUtil implements ApplicationContextAware {
 
    private static ApplicationContext applicationContext;
 
    /**
     * 加载Spring配置文件时,如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware接口,会自动调用该方法
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationUtil.applicationContext = applicationContext;
    }
 
    /**
     * 获取Spring上下文
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
 
    /**
     * 获取Spring Bean
     * @param name
     * @return
     * @throws BeansException
     */
    public static Object getBean(String name) throws BeansException {
        return applicationContext.getBean(name);
    }
}

手动获取ServletContext

@Component
public class Test {
    @Autowired
    private WebApplicationContext webApplicationContext;
    
    public ServletContext get(){
        ServletContext servletContext = webApplicationContext.getServletContext();
        return servletContext;
    }
}

关于上下文问题

spring存在一个ApplicationContext容器,servlet技术存在一个ServletContext容器,ApplicationContext中含有ServletContext。

监听器

@Component
public class MyServerContextListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 建立映射表
        Map<String, String> map = new HashMap<>();
        ApplicationContext applicationContext = event.getApplicationContext();
        ServletContext bean = applicationContext.getBean(ServletContext.class);
        // 将映射表放入上下文域中
        bean.setAttribute("myMap",map);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值