0
点赞
收藏
分享

微信扫一扫

【Java21】spring:AOP,事务控制,转账业务,web工程监听器

狗啃月亮_Rachel 2022-04-04 阅读 33

文章目录


1.转账业务

在这里插入图片描述

1.1 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>spring_day03_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>
</project>
//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:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
    </bean>

    <bean id="factoryBean" class="com.itheima.utils.ProxyBeanFactory">
        <property name="accountService" ref="accountService"></property>
    </bean>
    <bean id="proxyAccountService" factory-bean="factoryBean" factory-method="getAccountServiceProxyObject"></bean>
</beans>
//jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/spring_day02
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root

1.2 service

package com.itheima.service;

public interface AccountService {
    public void transfer(String outName,String inName,double money);
    public void update();
    public void delete();
}
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void update() {
        System.out.println("更新");
    }
    @Override
    public void delete() {
        System.out.println("删除");
    }
    @Override
    public void transfer(String outName, String inName, double money) {
    	//对update,delete,transfer三个方法都加上事务,用动态代理对AccountServiceImpl进行增强
       //创建AccountServiceImpl对象的代理对象, 我们调用这些update方法执行代理对象的方法。
      //动态代理方式没有impl实现类,怎么装到spring容器中?以前不管是注解还是bean标签都要求有实现类
      //装配对象即创建对象的三种方式
//        try {
//            System.out.println("开启事务");
            //1.查询付款人的账户信息
            Account outAccount = accountDao.findAccountByName(outName);
            //2.查询收款人的账户信息
            Account inAccount = accountDao.findAccountByName(inName);
            //对2人的账户进行添加和减少的操作
            outAccount.setMoney(outAccount.getMoney()-money);
            inAccount.setMoney(inAccount.getMoney()+money);

            //3.将更新后的账户信息更新到数据库
            accountDao.update(outAccount);
            int i = 1/0;
            accountDao.update(inAccount);
//            System.out.println("提交事务");
//        } catch (Exception e) {
//            System.out.println("回滚事务");
//            e.printStackTrace();
//        }
    }
}

1.3 dao

package com.itheima.dao;
import com.itheima.domain.Account;

public interface AccountDao {
    public Account findAccountByName(String name);
    public void update(Account account);
}
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate ;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    @Override
    public Account findAccountByName(String name) {
        String sql = "select * from account where name = ?";
        Account account = null;
        try {
            account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
        } catch (DataAccessException e) {
            e.printStackTrace();
        }
        return account;
    }
    @Override
    public void update(Account account) {
        String sql = "update account set money = ? where name = ?";
        jdbcTemplate.update(sql,account.getMoney(),account.getName());
    }
}

1.4 test

package com.itheima.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
    @Resource(name = "proxyAccountService")
    private AccountService accountService;
    @Test
    public void transfer() {
        String outName = "aaa";
        String inName ="bbb";
        double money = 500;
        accountService.transfer(outName,inName,money);
    }
    @Test
    public void update(){
        accountService.update();
    }
    @Test
    public void delete(){
        accountService.delete();
    }
}

transfer执行结果如下:
在这里插入图片描述

2.jdk动态代理原理

package com.itheima.proxy;

public interface IActor {
    public void sing();
    public void active();
}
package com.itheima.proxy;

public class ActorImpl implements IActor {
    @Override
    public void sing() {
        System.out.println("唱歌");
    }
    @Override
    public void active() {
        System.out.println("表演");
    }
}

动态代理:不修改源码情况下,对现有方法进行增强,要增强的功能只要写一遍就可以了。

package com.itheima.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyTest {
    public static void main(String[] args) {
        IActor actor = new ActorImpl();  //需要创建一个对象来代理需要增强的actor对象
        /**
         *  参数1:类加载器,一般写被代理对象的类加载器
         *  参数2:接口,被代理对象实现的接口。
         *  参数3:处理器
         */
        IActor iActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(),
                new InvocationHandler() {   //选中方法名,按ctrl+q查看方法注释
                    /**
                     * @param proxy  被代理对象本身。没用。
                     * @param method 被调用的方法。
                     * @param args   调用方法时传递的参数
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("增强的功能");
                        /**
                         *  参数1:方法调用时依赖的对象
                         *  参数2:调用方法时需要传递的参数
                         */                       
                        return method.invoke(actor,args);  //调用原有方法的功能
                    }
                });

        iActor.sing();
        iActor.active();
        
//        IActor actor2 = new $Proxy0();
//        actor2.sing();
//        actor2.active();
    }
}

在这里插入图片描述

3.cglib动态代理

jdk动态代理产生iActor对象必须要有接口IActor,但很多时候我们要增强的对象ActorImpl没有接口IActor ,用cglib动态代理。
在这里插入图片描述

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.1</version>
</dependency>
package com.itheima.proxy;

public class Actor2 {
    public void dance(){
        System.out.println("跳舞");
    }
}
package com.itheima.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibProxyTest {
    public static void main(String[] args) {
        /**
         *  参数1: 需要代理的实现类。
         *  参数2: 回调函数,作用和jdk动态代理中的InvocationHandler一模一样。
         */
       Actor2 actor2 = (Actor2)Enhancer.create(Actor2.class, new MethodInterceptor() {
           /**
            *  此方法和 jdk动态代理中的 处理器的  invoke方法一样
            * @param o   代理对象本身
            * @param method  将要调用的方法
            * @param objects  调用方法时传递的参数
            * @param methodProxy  代理后的方法。 没用
            */
           @Override
           public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
               System.out.println("功能的增强");               
               return method.invoke(new Actor2(),objects); //调用原有方法的功能
           }
       });  
        actor2.dance();
    }
}

在这里插入图片描述

4.动态代理实现service层事物的管理

要创建AccountServiceImpl实现类对象代理类对象对其进行增强:创建一个工具类:1.提供一个静态或者动态方法用来返回AccountServiceImpl的动态代理对象。2.并且使用工厂创建方式装配到spring容器中。

    <bean id="factoryBean" class="com.itheima.utils.ProxyBeanFactory">
        <property name="accountService" ref="accountService"></property>
    </bean>
    <bean id="proxyAccountService" factory-bean="factoryBean" factory-method="getAccountServiceProxyObject"></bean>
package com.itheima.utils;
import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyBeanFactory {
   private   AccountService accountService = null;
   public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }
   public  AccountService getAccountServiceProxyObject(){  //创建动态代理对象       
        AccountService proxyAccountService = (AccountService) Proxy.
                newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object obj = null;
                        try {
                            System.out.println("开启事务");                            
                            obj = method.invoke(accountService,args); //这一行原有功能不变
                            System.out.println("提交事务");
                        } catch (Exception e) {
                            System.out.println("回滚事务");
                            e.printStackTrace();
                        }
                        return obj;
                    }
                });
        return proxyAccountService;
    }
}
package com.itheima.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
    @Resource(name = "proxyAccountService")
    private AccountService accountService;
    @Test
    public void transfer() {
        String outName = "aaa";
        String inName ="bbb";
        double money = 500;
        accountService.transfer(outName,inName,money);
    }
    @Test
    public void update(){
        accountService.update();
    }
    @Test
    public void delete(){
        accountService.delete();
    }
}

update执行结果如下:
在这里插入图片描述

5.aop入门

在这里插入图片描述
如下printLog()比作System.out.println(“功能的增强”);的封装。Aspect就是Advice加在joinPoint的前面还是后面等。
在这里插入图片描述
使用AOP完整一个快速入门,在业务层的前后加上日志的输出,掌握spring的xml方式的AOP配置。
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>spring_day03_02</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <!-- ioc相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!-- spring单元测试 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        
        <!-- aop相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
    </dependencies>
</project>
package com.itheima.service;

public interface UserService {
    public void findUserById();
}
package com.itheima.service.impl;
import com.itheima.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void findUserById() {
        System.out.println("根据用户的id查询用户的信息");
        //int i = 1/0;
    }
}
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;

public class LogUtils { //增强
    public void printLog(){
        System.out.println("打印日志信息.......");
    }
    public void printLog2(){
        System.out.println("打印日志信息2222222.......");
    }
    public void printLog3(){
        System.out.println("打印日志信息3333333.......");
    }    
    public void printLog4(ProceedingJoinPoint point){ //@param point 环绕通知必要的参数 ,切入点。
        try {
            System.out.println("自定义环绕通知,开启事务。");  //这一行作用和前置通知一样          
            point.proceed(); //调用原有功能的方法,在原有功能基础上自己定义在哪个地方增强。
            System.out.println("自定义环绕通知,提交事务");  //这一行作用和后置通知一样
        } catch (Throwable throwable) {
            System.out.println("自定义环绕通知,回滚事务");  //这一行作用和异常通知一样
            throwable.printStackTrace();
        }finally {
            System.out.println("自定义环绕通知,释放资源");  //这一行作用和最终通知一样
        }
    }
}

接下里创建代理对象将LogUtils中方法应用到UserServiceImpl中:以前自己写JDK或cglib动态代理实现,现在只要配置告诉spring(spring自动根据是否有实现类,使用jdk动态代理还是cglib动态代理)。

//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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
       
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>

    <!-- 将增强所在的类,创建一个对象,并且装配到spring容器 -->
    <bean id="logUtils" class="com.itheima.utils.LogUtils"></bean>
    
    <!-- 配置aop -->
    <!-- 声明开始配置aop -->
    <aop:config>
        <!-- 切面:具体配置切入点和增强的关系的
                id: 切点的唯一标识,不需要
                ref: 指定增强所在的对象,增强方法前提是new出一个对象
        -->
        <aop:aspect ref="logUtils">
            <!-- 切入点和增强
                    method:  指的就是增强所在的方法
                    pointcut: 切入点,将要被增强的方法                    

                      AspectJ表达式语法: 修饰符  返回值   包名.类名.方法名()         
                      1.修饰符可以省略
                          execution(void com.itheima.service.impl.UserServiceImpl.findUserById())
                      2. *可以代表任意返回值
                        execution(* com.itheima.service.impl.UserServiceImpl.findUserById())
                      3. *可以代表任意包名,有一级包,就必须有一个*
                        execution(* *.*.*.*.UserServiceImpl.findUserById())
                      4. ..代表任意包和多级包
                          execution(* *..UserServiceImpl.findUserById())
                      5. * 可以代表任意类名
                        execution(* *..*.findUserById())
                      6. * 可以代表任意方法名
                        execution(* *..*.*())
                      7. * 可以代表任意参数,必须有参数
                        execution(* *..*.*(*))
                      8.   .. 可以代表任意参数,包括无参
                       execution(* *..*.*(..))

                      实际企业开发中,常用的AspectJ表达式是:
                      execution(* com.itheima.service.impl.*.*(..))
             -->             
            <!-- 抽取切入点,方便复用
                    expression:切入点的AspectJ表达式
                    id:切入点的唯一标识
             -->
            <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
            <!--<aop:before method="printLog" pointcut-ref="pt1"></aop:before>--> <!--pt1就是上一行-->
            <!--<aop:before method="printLog2" pointcut-ref="pt1"></aop:before>-->
            <!--<aop:before method="printLog3" pointcut-ref="pt1"></aop:before>-->
            <!--
                  <aop:before> : 前置通知,增强在核心功能之前执行。
                  <aop:after-returning> : 后置通知,在核心功能之后执行.如果出现异常,后置通知不会执行
                  <aop:after-throwing>: 异常通知,当出现异常时,执行通知。
                  <aop:after> : 最终通知,无论是否有异常,都会执行通知
                  <aop:around> : 环绕通知,自定义通知的位置
             -->
           <!-- <aop:after-returning method="printLog" pointcut-ref="pt1"></aop:after-returning>-->
            <!--<aop:after-throwing method="printLog" pointcut-ref="pt1"></aop:after-throwing>-->
            <!--<aop:after method="printLog" pointcut-ref="pt1"></aop:after>-->
            <aop:around method="printLog4" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.AOP半注解半xml:applicationContext.xml,UserService.java,UserServiceImpl.java,LogUtils.java,UserServiceTest.java

//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"
       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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 开启IOC注解扫描的包 -->
        <context:component-scan base-package="com.itheima"></context:component-scan>
        <!-- 开启注解AOP -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
package com.itheima.service;

public interface UserService {
    public void findUserById();
}
package com.itheima.service.impl;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void findUserById() {
        System.out.println("根据用户的id查询用户的信息");
        //int i = 1/0;
    }
}
package com.itheima.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component  // 当前增强类装配到spring容器
@Aspect  // 声明当前类是一个切面类
public class LogUtils {
    /**
     *  xml中开启aop注解方式:
     *       <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
     *  在通知类上添加
     *     @Aspect 声明当前类是一个切面类
     *     @Component //当前增强类装配到spring容器
     *
     *  @Before : 配置前置通知
     *  @AfterReturning: 配置后置通知
     *  @After: 配置最终通知
     *  @Around : 配置环绕通知
     *  @AfterThrowing: 配置异常通知
     *
     *  注解方式抽取切入点:
     *      1.声明一个空实现的方法:pt1()
     *      2.在方法上添加@Pointcut注解,value属性值就是切入点的AspectJ表达式
     *      3.方法名就是切入点的 id
     */
    //@Before("execution(* com.itheima.service.impl.*.*(..))")
    public void printLog(){
        System.out.println("前置通知");
    }
    //@AfterReturning("execution(* com.itheima.service.impl.*.*(..))")
    public void printLog2(){
        System.out.println("后置通知");
    }
    //@AfterThrowing("execution(* com.itheima.service.impl.*.*(..))")
    public void printLog3(){
        System.out.println("异常通知");
    }
    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    public void pt1(){
    }
    @Around("pt1()")
    public void printLog4(ProceedingJoinPoint point){
        try {
            System.out.println("自定义环绕通知,开启事务。");            
            point.proceed();  //调用原有功能的方法。
            System.out.println("自定义环绕通知,提交事务");
        } catch (Throwable throwable) {
            System.out.println("自定义环绕通知,回滚事务");
            throwable.printStackTrace();
        }finally {
            System.out.println("自定义环绕通知,释放资源");
        }
    }
}
package com.itheima.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.swing.*;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserServiceTest {
    @Autowired
    private UserService userService;
    @Test
    public void findUserById() {
        userService.findUserById();
    }
}

在这里插入图片描述

7.纯注解开发

package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.itheima")
public class SpringConfig {
    /**
     *  开发步骤:  1. @Configuration 标注当前类是一个配置类
     *             2.@ComponentScan 开启注解扫描(ioc)
     *             3.@EnableAspectJAutoProxy 开启注解aop即可
     */
}
package com.itheima.service;
import com.itheima.config.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.swing.*;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
    @Autowired
    private UserService userService;
    @Test
    public void findUserById() {
        userService.findUserById();
    }
}

8.JdbcTemplate

8.1 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>spring_day04_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        
        <!--IOC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>

        <!--jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        
        <!-- 事务相关的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>

        <!-- aop 相关依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
    </dependencies>
</project>
<?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 http://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 http://www.springframework.org/schema/aop/spring-aop.xsd">
       
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
       <!-- <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="url" value="${jdbc.url}"></property>
    </bean>


    <!-- spring事务的配置步骤
        1.在applicationContext.xml中配置事务管理器的bean
        2.配置事务的策略
        3.配置事务的aop
    -->
    <!--
        声明对于jdbc进行事务管理的 事务管理器对象
        id建议叫做  transactionManager,如果不叫这个名字,我们配置策略是,需要指定事务管理器对象的名字
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--
        配置spring事务的策略
        tx:advice: 声明开始配置事务的策略
            id: 当前策略的唯一标识
            transaction-manager: 指定事务管理器对象。默认值为transactionManager
    -->
    <tx:advice id="tx1" >
        <!-- 声明具体的策略 -->
        <tx:attributes>
            <!--
                tx:method : 指定对于哪些方法进行事务的管理
                        name:方法名的表达式
                        read-only: 事务是否只读,默认是false
                        timeout:超时时间,默认为-1,永不超时。
                        isolation: 事务的隔离级别,默认采用数据库默认的隔离级别
                        propagation: 事务的传播行为
                        rollback-for="": 针对指定的异常进行回滚
                        no-rollback-for="": 针对指定的异常不进行回滚
            -->
            <tx:method name="*" />
           <!-- <tx:method name="select*" read-only="true"/>-->
        </tx:attributes>
    </tx:advice>

    <!-- 配置事务的aop
        AspectJ 表达式的语法  修饰符 返回值 包名.类型.方法名()
    -->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
        <!--
            advice-ref="tx1":使用哪个策略
            pointcut-ref="pt1" : 事务切入到哪个切入点
        -->
        <aop:advisor advice-ref="tx1" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
</beans>
//jdbc.properties
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring_day02
jdbc.driverClass=com.mysql.jdbc.Driver

8.2 domain

package com.itheima.domain;

public class Account {
    private int id;
    private String name;
    private double money;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

8.3 dao

package com.itheima.dao;
import com.itheima.domain.Account;

public interface AccountDao {
    public Account findAccountByName(String name);
    public void update(Account account);
}
package com.itheima.dao;
import org.springframework.jdbc.core.JdbcTemplate;

public class BaseDao {
    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
}

任意dao用到jdbcTemplate的话都要声明这对象,冗余复杂。所以抽成父类BaseDao被继承。
在这里插入图片描述

package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.dao.BaseDao;
import com.itheima.domain.Account;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import java.sql.Connection;
import java.sql.Savepoint;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { //BaseDao换成JdbcDaoSupport 
    @Override
    public Account findAccountByName(String name) {
        String sql = "select * from account where name = ?";
        Account account = null;
        try {
            account = getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
        } catch (DataAccessException e) {
            e.printStackTrace();
        }
        return account;
    }
    @Override
    public void update(Account account) {
        String sql = "update account set money = ? where name = ?";
        getJdbcTemplate().update(sql, account.getMoney(), account.getName());
    }
}

8.4 service

package com.itheima.service;
import com.itheima.domain.Account;

public interface AccountService {
    public Account findAccountByName(String name);
    public void transfer(String outName,String inName,double money);
}
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public Account findAccountByName(String name) {
        return accountDao.findAccountByName(name);
    }
    @Override
    public void transfer(String outName, String inName, double money) {
        //1.查询用户的账户信息
        Account outAccount = accountDao.findAccountByName(outName);
        Account inAccount = accountDao.findAccountByName(inName);
        //2.转账,
        outAccount.setMoney(outAccount.getMoney()-money);
        inAccount.setMoney(inAccount.getMoney()+money);
        //3.将数据更新到数据库
        accountDao.update(outAccount);
        int i = 1/0;
        accountDao.update(inAccount);
    }
}

8.5 test

package com.itheima.service;
import com.itheima.domain.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;
    @Test
    public void findAccountByName() {
        Account aaa = accountService.findAccountByName("aaa");
        System.out.println(aaa);
    }
    @Test
    public void transfer(){
        accountService.transfer("aaa","bbb",200);
    }
}

在这里插入图片描述

问题:使用JdbcDaoSupport能够使用注解的方式吗?不能,无法将dataSouce注入到JdbcDaoSupport中(源码不能加注解)。

jdbcTemplate和JdbcDaoSupport的区别?
1.在dao层定义jdbcTemplate可以使用注解和xml配置方式。而且jdbcTemplate需要被spring的容器管理。
2.继承JdbcDaoSupport的话,这种方式只适用于xml配置方式,并且,只需要在applicationContext.xml中将dataSource注入给dao的实现类即可。不需要管理jdbcTemplate对象。

9.事务控制

第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
第二:spring框架为我们提供了一组事务控制的接口。这组接口是在spring-tx-4.3.13.RELEASE.jar中。
第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。重点是使用配置的方式实现。有了spring后,事务就从编程中脱离出来。

PlatformTransactionManage接口是平台事务管理器,是Spring真正管理事务的对象,常用实现类DataSourceTransactionManager(针对JDBC和mybatis事务管理)。
在这里插入图片描述
在这里插入图片描述

9.1 TransactionDefinition:

事务定义对象有如下api:
在这里插入图片描述

9.1.1 定义事务隔离级别

事务四大特性:1.原子性(A):如转账,一个人转出,一个人转入,这两步操作是不能分的,要么都做,要么都不做。
2.一致性(C):事务的成功与否,数据库的数据都满足生活正常逻辑,如转账,总额不会变。
3.隔离性(I):并发访问数据库时,一个用户的事务不被其他事务干扰。
4.持久性(D):事务提交后,它对数据库的改变是持久的,断电也变。

注意隔离性,虽然事务间没有影响,但两个事务同时并发且操作同一条数据会出现如下问题:
1.脏读:一个事务读到另一个事务未提交的数据。
2.不可重复读:事务A第一次读账户余额为1000,事务B这时操作了这条数据并且提交+1000的事务。事务A又去读,变为2000了,读不到重复的1000了。
3.幻读:不可重复读强调读取内容【select *,update】不一致,幻读强调读取数量【select count(*),delete或insert】不一致。

用事务隔离级别解决如上问题:
read uncommitted:不会去设的,未提交会出现脏读。
read committed:解决脏读,还是存在不可重复读和幻读。
repeatable-read:mysql数据库默认隔离级别,解决不可重复读,还是存在幻读。
serialize:串行化,解决所有。

上面4个,spring在TransactionDefinition设置事务隔离级别时定义如下5个值:多了一个DEFAULT,默认采用数据库隔离级别。
在这里插入图片描述

9.1.2 事务传播行为

事务传播行为解决的问题: 一个业务层事务 调用 另一个业务层事务时,事务之间关系如何处理。
在这里插入图片描述
事务传播行为PROPAGATION的取值:

REQUIRED: 一个事务, 要么都成功,要么都失败。
REQUIRES_NEW :两个不同事务,彼此之间没有关系 一个事务失败了 不影响另一个事务。
NESTED :一个事务, 在A事务 调用 B过程中, B失败了, 回滚事务到 之前SavePoint , 用户可以选择提交事务或者回滚事务。

在这里插入图片描述

10.spring的xml方式配置事务: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" 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 http://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 http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
       <!-- <property name="jdbcTemplate" ref="jdbcTemplate"></property>-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="url" value="${jdbc.url}"></property>
    </bean>

    <!-- spring事务的配置步骤
        1.在applicationContext.xml中配置事务管理器的bean
        2.配置事务的策略
        3.配置事务的aop
    -->
    <!--
        声明对于jdbc进行事务管理的 事务管理器对象
        id建议叫做  transactionManager,如果不叫这个名字,我们配置策略时,需要指定事务管理器对象的名字
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <!--
        配置spring事务的策略
        tx:advice:声明开始配置事务的策略
            id:当前策略的唯一标识
            transaction-manager: 指定事务管理器对象。默认值为transactionManager
    -->
    <tx:advice id="tx1" >
        <!-- 声明具体的策略 -->
        <tx:attributes>
            <!--
                tx:method : 指定对于哪些方法进行事务的管理
                        name:方法名的表达式
                        read-only: 事务是否只读,默认是false
                        timeout:超时时间,默认为-1,永不超时。
                        isolation: 事务的隔离级别,默认采用数据库默认的隔离级别
                        propagation: 事务的传播行为
                        rollback-for="": 针对指定的异常进行回滚
                        no-rollback-for="": 针对指定的异常不进行回滚
            -->
            <tx:method name="*" />
           <!-- <tx:method name="select*" read-only="true"/>-->
        </tx:attributes>
    </tx:advice>


    <!-- 配置事务的aop
        AspectJ 表达式的语法  修饰符 返回值 包名.类型.方法名()
    -->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
        <!--
            advice-ref="tx1":使用哪个策略
            pointcut-ref="pt1" : 事务切入到哪个切入点
        -->
        <aop:advisor advice-ref="tx1" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
</beans>

11.spring的半xml半注解方式配置事务

ioc注解要用包扫描开启ioc注解,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: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 http://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 http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- bean definitions here -->

    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>



    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>


    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="url" value="${jdbc.url}"></property>
    </bean>


    <!--
        1.开启注解方式的事务
        2.开启ioc注解
        3.在service层的实现类上添加@Transactional 即可

             @Transactional: 即可用在方法上,也可用在实现类上,也可用在接口上。
                    执行方法是,寻找的顺序是   方法->实现类->接口
                  如果需要配置事务的信息,那么直接通过@Transactional注解的相关属性去配置事务的策略信息
    -->

    <context:component-scan base-package="com.itheima"></context:component-scan>
    <tx:annotation-driven></tx:annotation-driven>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


</beans>
package com.itheima.service.impl;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")

public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public Account findAccountByName(String name) {
        return accountDao.findAccountByName(name);
    }

    @Override
    @Transactional
    public void transfer(String outName, String inName, double money) {
        //1.查询用户的账户信息
        Account outAccount = accountDao.findAccountByName(outName);
        Account inAccount = accountDao.findAccountByName(inName);
        //2.转账,
        outAccount.setMoney(outAccount.getMoney()-money);
        inAccount.setMoney(inAccount.getMoney()+money);
        //3.将数据更新到数据库
        accountDao.update(outAccount);
        int i = 1/0;
        accountDao.update(inAccount);

    }
}

12.spring的纯注解方式配置事务:AccountServiceTest.java,JdbcConfig.java

package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
     需要使用一个配置类来代替xml核心配置
     1.创建一个类,通过@Configuration来声明当前类是一个配置类
     2.引入外部配置文件
     3.将jdbcTemplate和dataSource,和事务管理器对象 装配到spring容器中
     4.开启包扫描
     5.开启注解事务
 */
@Configuration
@Import(JdbcConfig.class)
@ComponentScan("com.itheima")
@EnableTransactionManagement  //开启注解事务
public class SpringConfig {
}
package com.itheima.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;

@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
	@Value("${jdbc.username}")
	private String username;
	@Value("${jdbc.password}")
	private String password;
	@Value("${jdbc.url}")
	private String url;
	@Value("${jdbc.driverClass}")
	private String driverClass;
    //下面是三个对象的装配	
	@Bean
	public DataSource dataSource(){
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driverClass);
		dataSource.setUrl(url);
		dataSource.setPassword(password);
		dataSource.setUsername(username);
		return dataSource;
	}
	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource){
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		return jdbcTemplate;
	}
	@Bean
	public DataSourceTransactionManager transactionManager(DataSource dataSource){
		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
		transactionManager.setDataSource(dataSource);
		return transactionManager;
	}
}
package com.itheima.service;
import com.itheima.config.SpringConfig;
import com.itheima.domain.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;
    @Test
    public void findAccountByName() {
        Account aaa = accountService.findAccountByName("aaa");
        System.out.println(aaa);
    }
    @Test
    public void transfer(){
        accountService.transfer("aaa","bbb",200);
    }
}

13. 配置文件:pom.xml,applicationContext.xml,web.xml

//pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>spring_day04_web</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <!--监听器与web相关-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
    </dependencies>

    <packaging>war</packaging>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
//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">
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>
//web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
           version="3.0">
  <!--
    <context-param>
        <param-name>classPath</param-name>
        <param-value>applicationContext.xml</param-value>
    </context-param>    
    
    <listener>
        <listener-class>com.itheima.listener.MyServletContextListener</listener-class>
    </listener>
-->

    <!-- 告诉spring的监听器,核心配置文件的位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    
    <!-- spring的监听器,不是上面自定义的-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

14.service:UserService.java,UserServiceImpl.java

package com.itheima.service;

public interface UserService {
    public void login();
}
package com.itheima.service.impl;
import com.itheima.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void login() {
        System.out.println("处理登录业务");
    }
}

15.web:DemoServlet.java

package com.itheima.web;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/demoServlet")
public class DemoServlet extends HttpServlet {
    private UserService userService;
    @Override
    public void init() throws ServletException {
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         *  问题:
         *      1.如果有多个servlet,那么这个容器就被创建了多次。而spring容器只需要创建一次即可。
         *      2.代码冗余
         *
         *   最主要解决的问题:
         *      1.我们需要创建一个spring容器,并且只创建一次。
         *      2.创建的spring容器应改被所有的servlet所共有。  servletContext 域对象。
         *      3.这个容器应该随着服务器启动就自动创建。  servletContextListener监听器  或者servlet的启动时加载<load-on-startup></>
         */         
        //创建spring容器
        //第一次:自定义容器
        //ApplicationContext ac = (ApplicationContext) getServletContext().getAttribute("ac");
        
        //第二次:使用spring的容器
        //ApplicationContext ac = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        
        //第三次: 优化后的
        ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        userService = (UserService) ac.getBean("userService");
        userService.login();
    }
}

在这里插入图片描述
在这里插入图片描述

16.listener:MyServletContextListener.java

package com.itheima.listener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyServletContextListener implements ServletContextListener {
    /**
     *  ServletContextListener:用来监听servletContext对象的创建和销毁的
     *      servletContext:随着服务器启动创建,随着服务器销毁而销毁
     */
    /**
     *  如果监听到servletContext对象创建,调用当前方法
     * @param servletContextEvent   事件监听对象,保存着被监听的对象对象
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
//        ServletContext servletContext = servletContextEvent.getServletContext();
//        System.out.println("servletContext对象创建了");
        System.out.println("初始化spring容器,并且将spring容器保存到servletContext域中");
        ServletContext servletContext = servletContextEvent.getServletContext();

        //servletContext对象保存的是当前项目相关的信息,能读取web.xml
        String classPath = servletContext.getInitParameter("classPath");
        System.out.println("读取web.xml中配置的全局的初始化参数:"+classPath);
        
        //1.创建spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(classPath);
        //2.添加到servletContext域中
        servletContext.setAttribute("ac",ac);
    }

    /**
     * 如果监听到servletContext对象销毁,调用当前方法
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("servletContext对象销毁了");
    }
}

在这里插入图片描述

举报

相关推荐

0 条评论