关于spring框架,看这一篇就够了~~~

一、Spring是什么?

1. Spring定义

Spring 是分层的 full-stack(全栈) 轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 Spring MVC 和业务层事务管理等众多的企业级应⽤技术,还能整合开源世界众多著名的第三方框架和类库,已 经成为使⽤最多的 Java EE 企业应⽤开源框架。

2. Spring的优点

  • 避免硬编码造成代码过度耦合。用户不必再为单例模式类、属性文件解析等底层需求编写代码,专注于上层应用。
  • AOP面向切面编程,支持横切逻辑插入
  • 声明式事务支持,极大方便和灵活的进行事务管理
  • 方便集成各种优秀框架

3. Spring的核心结构

Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数 据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块 和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决方案的零侵入的轻量级框架。

请添加图片描述

  • Spring核⼼容器(Core Container):容器是Spring框架最核⼼的部分,它管理着Spring应⽤中 bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。 基于bean⼯⼚,我们还会发现有多种Spring应用上下文的实现。所有的Spring模块都构建于核心容器之上。

  • ⾯向切⾯编程(AOP)/Aspects:Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应用系统中面向切⾯开发的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。

  • 数据访问和集成(Data Access/Integration)

    Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专 注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问 提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由 JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。

  • Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅ 案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。

  • Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测 试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。

4. Spring的核心思想

4.1 IOC和DI

  1. IOC和DI

    IoC :Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现

    • 描述的事情:Java开发领域对象的创建,管理的问题
    • 和传统创建对象的方式对比
      • 传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象
      • IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对 象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可

    DI:Dependancy Injection(依赖注⼊) ,指的是A对象依赖于B对象时,需要把B的实例对象注入给A

  2. 为什么使用IOC?

    当我们需要使用对象时,向Spring的IOC容器获取即可,这样的好处是解决了对象之间的耦合问题

    • 当需要使用bean对象时,如果使用多态创建对象,接口对象指定自己new的对象,有强耦合,若需要修改,则需要修改每一处创建当前对象的源代码。
    • 若使用IOC,则切换实现类只需要在IOC容器中替换注入的bean即可。

4.2 AOP

  1. 什么是AOP?

    AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程

    AOP解决的是多个方法中相同的位置出现代码重复的问题。比如:需要查询多个方法的执行时间,需要在方法执行前记录时间,方法结束时记录时间。这种代码就被称为横切逻辑代码。类似可以插入横切逻辑的地方有如:方法开始时、结束时、返回时、异常时等。

  2. 为什么使用AOP?

在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复


二、IOC和AOP的案例实现

1. 案例需求

实现简单的转账案例,从付款账户减去转账金额,收款账户添加转账金额

2. 案例实现

  1. 转账界面搭建

    请添加图片描述

    代码:

    <!doctype html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>转账汇款</title>
    
        <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
    
    
        <style type="text/css">
            body {
                /*background-color:#00b38a;*/
                text-align:center;
            }
    
            .lp-login {
                position:absolute;
                width:500px;
                height:300px;
                top:50%;
                left:50%;
                margin-top:-250px;
                margin-left:-250px;
                background: #fff;
                border-radius: 4px;
                box-shadow: 0 0 10px #12a591;
                padding: 57px 50px 35px;
                box-sizing: border-box
            }
    
    
            .lp-login .submitBtn {
                display:block;
                text-decoration:none;
                height: 48px;
                width: 150px;
                line-height: 48px;
                font-size: 16px;
                color: #fff;
                text-align: center;
                background-image: -webkit-gradient(linear, left top, right top, from(#09cb9d), to(#02b389));
                background-image: linear-gradient(90deg, #09cb9d, #02b389);
                border-radius: 3px
            }
    
    
            input[type='text'] {
                height:30px;
                width:250px;
            }
    
            span {
                font-style: normal;
                font-variant-ligatures: normal;
                font-variant-caps: normal;
                font-variant-numeric: normal;
                font-variant-east-asian: normal;
                font-weight: normal;
                font-stretch: normal;
                font-size: 14px;
                line-height: 22px;
                font-family: "Hiragino Sans GB", "Microsoft Yahei", SimSun, Arial, "Helvetica Neue", Helvetica;
            }
    
        </style>
        <script type="text/javascript">
            $(function(){
                $(".submitBtn").bind("click",function(){
                    var fromAccount = $("#fromAccount").val();
                    var toAccount = $("#toAccount").val();
                    var money = $("#money").val();
    
                    if(money == null || $.trim(money).length == 0){
                        alert("sorry,必须输入转账金额~");
                        return;
                    }
    
                    $.ajax({
                        url:'/transferServlet',
                        type:'POST',    //GET
                        async:false,    //或false,是否异步
                        data:{
                            fromCardNo:fromAccount.split(' ')[1],
                            toCardNo:toAccount.split(' ')[1],
                            money:money
                        },
                        timeout:5000,    //超时时间
                        dataType:'json', //返回的数据格式:json/xml/html/script/jsonp/text
                        success:function(data){
                            if("200" == data.status){
                                alert("转账成功~~~");
                            }else{
                                alert("转账失败~~~,message:" + data.message);
                            }
                        }
                    })
                })
            })
    
            //检查输入值是否为整数
            function checkFormat(obj){
                var reg = /^[0-9]+[0-9]*]*$/;
                if($.trim($(obj).val()).length>0){
                    if(!reg.test($(obj).val())){
                        alert("输入格式错误!请输整数!");
                        $(obj).val("");
                    }else{
                        $(obj).val(parseInt($(obj).val()));
                    }
                }
            }
        </script>
    </head>
    <body>
    
    
    <form>
        <table class="lp-login">
            <tr>
                <td align="right"><span>收款账户</span></td>
                <td align="center">
                    <input type="text" id="toAccount" value="韩梅梅 6029621011001" disabled></input>
                </td>
            </tr>
            <tr>
                <td align="right"><span>付款账户</span></td>
                <td align="center">
                    <input type="text" id="fromAccount" value="李大雷 6029621011000" disabled></input>
                </td>
            </tr>
            <tr>
                <td align="right"><span>转账金额</span></td>
                <td align="center">
                    <input type="text" id="money" onblur="checkFormat(this)"></input>
                </td>
            </tr>
            <tr align="center">
                <td colspan="2">
                    <a href="javasrcipt:void(0)" class="submitBtn"><span>转 出</span></a>
                </td>
            </tr>
        </table>
    </form>
    
    </body>
    </html>
    
    

    注意:需要导入jquery包

  2. 创建数据库

    请添加图片描述

  3. 创建maven的web工程,引入pom依赖

        <!-- mysql数据库驱动包 -->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.35</version>
        </dependency>
        <!--druid连接池-->
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.21</version>
        </dependency>
    
        <!-- servlet -->
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
    
        <!-- jackson依赖 -->
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.6</version>
        </dependency>
    
  4. 创建pojo类,包含Account类 和 Result类,Account对应数据库的字段,Result类对应返回结果 和 异常时输出的信息

    Account类:

    package com.lb.pojo;
    
    public class Account {
    
        private String cardNo;
    
        private String name;
    
        private int money;
    
        public String getCardNo() {
            return cardNo;
        }
    
        public void setCardNo(String cardNo) {
            this.cardNo = cardNo;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
        @Override
        public String toString() {
            return "Account{" +
                    "cardNo='" + cardNo + '\'' +
                    ", name='" + name + '\'' +
                    ", money=" + money +
                    '}';
        }
    }
    

    Result类:

    package com.lb.pojo;
    
    public class Result {
    
        private String status;
        private String message;
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        @Override
        public String toString() {
            return "Result{" +
                    "status='" + status + '\'' +
                    ", message='" + message + '\'' +
                    '}';
        }
    }
    
  5. 创建DruidUtils工具类,获取数据库连接

    package com.lb.utils;
    
    import com.alibaba.druid.pool.DruidDataSource;
    
    public class DruidUtils {
    
        private DruidUtils(){}
    
        private static DruidDataSource druidDataSource = new DruidDataSource();
    
        static {
            druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
            druidDataSource.setUrl("jdbc:mysql:///bank");
            druidDataSource.setUsername("root");
            druidDataSource.setPassword("root");
        }
    
        public static DruidDataSource getInstance(){
            return druidDataSource;
        }
    
    
    }
    
    
  6. 创建AccountDao层接口 及 基于jdbc的实现类,实现通过卡号获取数据库中的钱数、通过卡号更新钱数

    接口:

    package com.lb.dao;
    
    import com.lb.pojo.Account;
    
    public interface AccountDao {
    
        Account queryAccountByCardNo(String cardNo) throws Exception;
    
        int updateAccountByCardNo(Account account) throws Exception;
    }
    

    实现类:

    package com.lb.dao.impl;
    
    import com.alibaba.druid.pool.DruidPooledConnection;
    import com.lb.dao.AccountDao;
    import com.lb.pojo.Account;
    import com.lb.utils.DruidUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    public class JdbcAccountDaoImpl implements AccountDao {
        @Override
        public Account queryAccountByCardNo(String cardNo) throws Exception {
            Connection connection = DruidUtils.getInstance().getConnection();
    
            String sql = "select * from account where cardNo = ?";
    
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, cardNo);
            ResultSet resultSet = preparedStatement.executeQuery();
    
            Account account = new Account();
            while (resultSet.next()){
                account.setCardNo(resultSet.getString("cardNo"));
                account.setName(resultSet.getString("name"));
                account.setMoney(resultSet.getInt("money"));
    
            }
    
            resultSet.close();
            preparedStatement.close();
            connection.close();
    
            return account;
    
        }
    
        @Override
        public int updateAccountByCardNo(Account account) throws Exception {
    
            Connection connection = DruidUtils.getInstance().getConnection();
    
            String sql = "update account set money = ? where cardNo = ?";
    
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, account.getMoney());
            preparedStatement.setString(2, account.getCardNo());
    
            int i = preparedStatement.executeUpdate();
    
            preparedStatement.close();
            connection.close();
    
            return i;
        }
    }
    
  7. 创建TransferService接口 及 实现类,实现转账的逻辑

    接口:

    package com.lb.service;
    
    public interface TransferService {
    
        public void transfer(String fromCardno, String toCardNo, int money) throws Exception;
    
    }
    

    实现类:

    package com.lb.service.impl;
    
    import com.lb.dao.AccountDao;
    import com.lb.dao.impl.JdbcAccountDaoImpl;
    import com.lb.pojo.Account;
    import com.lb.service.TransferService;
    
    public class TransferServiceImpl implements TransferService {
    
        private AccountDao accountDao = new JdbcAccountDaoImpl();
    
        @Override
        public void transfer(String fromCardno, String toCardNo, int money) throws Exception {
    
            Account from = accountDao.queryAccountByCardNo(fromCardno);
            Account to = accountDao.queryAccountByCardNo(toCardNo);
    
            from.setMoney(from.getMoney() - money);
            to.setMoney(to.getMoney() + money);
    
            accountDao.updateAccountByCardNo(from);
            accountDao.updateAccountByCardNo(to);
    
        }
    }
    
  8. 创建TransferServlet 及 json工具类JsonUtils ,实现获取界面输入的双方转账数目,通过调用TransferService的实现类实现转账功能

    package com.lb.servlet;
    
    import com.lb.pojo.Result;
    import com.lb.service.TransferService;
    import com.lb.service.impl.TransferServiceImpl;
    import com.lb.utils.JsonUtils;
    
    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(name = "transferServlet", urlPatterns = "/transferServlet")
    public class TransferServlet extends HttpServlet {
    
        private TransferService transferService = new TransferServiceImpl();
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            req.setCharacterEncoding("UTF-8");
    
            String fromCardNo = req.getParameter("fromCardNo");
            String toCardNo = req.getParameter("toCardNo");
            String moneyStr = req.getParameter("money");
            int money = Integer.parseInt(moneyStr);
    
            Result result = new Result();
            try {
                transferService.transfer(fromCardNo, toCardNo, money);
                result.setStatus("200");
            } catch (Exception e) {
                e.printStackTrace();
                result.setStatus("201");
                result.setMessage(e.toString());
            }
    
            resp.setContentType("application/json;charset=utf-8");
            resp.getWriter().print(JsonUtils.object2Json(result));
    
        }
    }
    
  9. 导入本地tomcat依赖,启动

3. 案例问题

  • 问题1:service层使用dao层接口的实现类时,直接new了实现类对象,若需要修改dao层的实现类,必须修改源代码,不符合面向接口开发的最优原则
  • 问题2:service层没有事务控制,若转账过程中发生异常,可能导致数据库数据错误

4. 解决思路

  • 问题1解决:

    将bean对象的实现类配置在xml文件中,结合工厂模式生产出保存所有bean的map容器进行统一管理,当程序启动时,容器就把对应的bean对象初始化管理起来。这样当要切换实现类时,只需要修改配置即可。

  • 问题2解决:

    • 将获取的数据库连接绑定到线程上,一个线程一个连接,用JDBC的Connection控制当前线程的事务执行。
    • 使用动态代理,管理Service层的事务

5. 代码改造

5.1手动添加自定义ioc容器

  1. 添加dom4j、xpath表达式的依赖

        <!--dom4j依赖-->
        <dependency>
          <groupId>dom4j</groupId>
          <artifactId>dom4j</artifactId>
          <version>1.6.1</version>
        </dependency>
        <!--xpath表达式依赖-->
        <dependency>
          <groupId>jaxen</groupId>
          <artifactId>jaxen</artifactId>
          <version>1.1.6</version>
        </dependency>
    
  2. 添加beans.xml,注册bean的全限定类名、属性等信息

    <?xml version="1.0" encoding="UTF-8" ?>
    <beans>
    
        <bean id = "transferService" class = "com.lb.service.impl.TransferServiceImpl">
            <property name = "AccountDao" ref = "accountDao"></property>
    
        </bean>
    
        <bean id = "accountDao" class="com.lb.dao.impl.JdbcAccountDaoImpl"></bean>
    
    </beans>
    
  3. 添加BeanFactory类,工厂方式实现bean对象的创建

    package com.lb.factory;
    
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class BeanFactory {
    
        //1、加载解析xml,读取xml中的bean信息,通过反射技术实例化bean对象,然后放⼊map待⽤
        //2、提供接⼝⽅法根据id从map中获取bean
    
        private static Map<String, Object> map = new HashMap<>();
    
        static {
            InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            SAXReader saxReader = new SAXReader();
    
            try {
                Document document = saxReader.read(resourceAsStream);
                Element rootElement = document.getRootElement();
                List<Element> list = rootElement.selectNodes("//bean");
    
                //实例化bean对象
                for (int i = 0; i < list.size(); i++) {
                    Element element = list.get(i);
                    String id = element.attributeValue("id");
                    String classPath = element.attributeValue("class");
    
                    Class<?> aClass = Class.forName(classPath);
                    Object o = aClass.newInstance();
                    map.put(id, o);
    
                }
    
                //维护bean之间的依赖关系
                List<Element> propertyNodes = rootElement.selectNodes("//property");
    
                for (int i = 0; i < propertyNodes.size(); i++) {
                    Element element = propertyNodes.get(i);
                    //处理property元素
                    String name = element.attributeValue("name");
                    String ref = element.attributeValue("ref");
    
                    String parentId = element.getParent().attributeValue("id");
                    Object parentObject = map.get(parentId);
                    Method[] methods = parentObject.getClass().getMethods();
                    for (int j = 0; j < methods.length; j++) {
                        Method method = methods[j];
                        if (("set" + name).equalsIgnoreCase(method.getName())){
                            //获取依赖的bean
                            Object propertyObject = map.get(ref);
                            //反射调用set方法,注入bean
                            method.invoke(parentObject, propertyObject);
                        }
    
                    }
    
                    //维护依赖关系后,重新将bean放入map
                    map.put(parentId, parentObject);
                }
    
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static Object getBean(String id){
            return map.get(id);
        }
    }
    
    
  4. 修改TransferServlet,从BeanFactory获取所需bean对象

    package com.lb.servlet;
    
    import com.lb.factory.BeanFactory;
    import com.lb.pojo.Result;
    import com.lb.service.TransferService;
    import com.lb.service.impl.TransferServiceImpl;
    import com.lb.utils.JsonUtils;
    
    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(name = "transferServlet", urlPatterns = "/transferServlet")
    public class TransferServlet extends HttpServlet {
    
        //private TransferService transferService = new TransferServiceImpl();
        private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            req.setCharacterEncoding("UTF-8");
    
            String fromCardNo = req.getParameter("fromCardNo");
            String toCardNo = req.getParameter("toCardNo");
            String moneyStr = req.getParameter("money");
            int money = Integer.parseInt(moneyStr);
    
            Result result = new Result();
            try {
                transferService.transfer(fromCardNo, toCardNo, money);
                result.setStatus("200");
            } catch (Exception e) {
                e.printStackTrace();
                result.setStatus("201");
                result.setMessage(e.toString());
            }
    
            resp.setContentType("application/json;charset=utf-8");
            resp.getWriter().print(JsonUtils.object2Json(result));
    
        }
    }
    
    
  5. 修改TransferServiceImpl,通过set方法实现dao层实例注入

    package com.lb.service.impl;
    
    import com.lb.dao.AccountDao;
    import com.lb.dao.impl.JdbcAccountDaoImpl;
    import com.lb.pojo.Account;
    import com.lb.service.TransferService;
    
    public class TransferServiceImpl implements TransferService {
    
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void transfer(String fromCardno, String toCardNo, int money) throws Exception {
    
            Account from = accountDao.queryAccountByCardNo(fromCardno);
            Account to = accountDao.queryAccountByCardNo(toCardNo);
    
            from.setMoney(from.getMoney() - money);
            to.setMoney(to.getMoney() + money);
    
            accountDao.updateAccountByCardNo(from);
            accountDao.updateAccountByCardNo(to);
    
        }
    }
    
    

5.2 手动实现转账事务管理

  1. 创建ConnectionUtils工具类,获取与当前线程绑定的数据库连接,用来事务管理。若当前线程存在,则直接获取;若不存在,则从连接池获取一个和当前线程绑定。

    package com.lb.utils;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class ConnectionUtils {
    
        private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    
        public Connection getCurrentThreadConn() throws SQLException{
    
            Connection connection = threadLocal.get();
            if (connection == null){
                connection = DruidUtils.getInstance().getConnection();
                threadLocal.set(connection);
            }
    
            return connection;
        }
    }
    
    
  2. 创建事务管理类,封装事务相关的开启、提交、回滚操作

    package com.lb.utils;
    
    import java.sql.SQLException;
    
    public class TransactionManager {
    
        private ConnectionUtils connectionUtils;
    
        public void setConnectionUtils(ConnectionUtils connectionUtils) {
            this.connectionUtils = connectionUtils;
        }
    
        //开启事务
        public void beginTransaction() throws SQLException {
            connectionUtils.getCurrentThreadConn().setAutoCommit(false);
        }
    
        //提交事务
        public void commit() throws SQLException{
            connectionUtils.getCurrentThreadConn().commit();
        }
    
        //回滚事务
        public void rollback() throws SQLException {
            connectionUtils.getCurrentThreadConn().rollback();
        }
    
    }
    
    
  3. 创建代理工厂类,获取目标对象的动态代理

    package com.lb.factory;
    
    import com.lb.utils.TransactionManager;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyFactory {
    
        private TransactionManager transactionManager;
    
        public void setTransactionManager(TransactionManager transactionManager) {
            this.transactionManager = transactionManager;
        }
    
        public Object getProxy(Object target){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                    Object res = null;
    
                    try{
                        //开启事务
                        transactionManager.beginTransaction();
                        //调用原方法
                        res = method.invoke(target, args);
                        //提交事务
                        transactionManager.commit();
    
                    }catch (Exception e){
                        e.printStackTrace();
                        //回滚事务
                        transactionManager.rollback();
                        //向上抛出异常,便于Servlet捕获
                        throw e.getCause();
                    }
    
                    return res;
                }
            });
        }
    
    }
    
    
  4. 修改TransferServlet,从bean工厂获取代理工厂的bean,从代理工厂获取transferService的jdk动态代理对象

    package com.lb.servlet;
    
    import com.lb.factory.BeanFactory;
    import com.lb.factory.ProxyFactory;
    import com.lb.pojo.Result;
    import com.lb.service.TransferService;
    import com.lb.service.impl.TransferServiceImpl;
    import com.lb.utils.JsonUtils;
    
    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(name = "transferServlet", urlPatterns = "/transferServlet")
    public class TransferServlet extends HttpServlet {
    
        //private TransferService transferService = new TransferServiceImpl();
        //private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
    
        private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
        private TransferService transferService = (TransferService) proxyFactory.getProxy(BeanFactory.getBean("transferService"));
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            req.setCharacterEncoding("UTF-8");
    
            String fromCardNo = req.getParameter("fromCardNo");
            String toCardNo = req.getParameter("toCardNo");
            String moneyStr = req.getParameter("money");
            int money = Integer.parseInt(moneyStr);
    
            Result result = new Result();
            try {
                transferService.transfer(fromCardNo, toCardNo, money);
                result.setStatus("200");
            } catch (Exception e) {
                e.printStackTrace();
                result.setStatus("201");
                result.setMessage(e.toString());
            }
    
            resp.setContentType("application/json;charset=utf-8");
            resp.getWriter().print(JsonUtils.object2Json(result));
    
        }
    }
    
    
  5. 修改JdbcAccountDaoImpl,注入ConnectionUtils依赖,从ConnectionUtils获取当前线程绑定的数据库连接。连接需要复用,取消连接关闭操作

    package com.lb.dao.impl;
    
    import com.alibaba.druid.pool.DruidPooledConnection;
    import com.lb.dao.AccountDao;
    import com.lb.pojo.Account;
    import com.lb.utils.ConnectionUtils;
    import com.lb.utils.DruidUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    public class JdbcAccountDaoImpl implements AccountDao {
        private ConnectionUtils connectionUtils;
    
        public void setConnectionUtils(ConnectionUtils connectionUtils) {
            this.connectionUtils = connectionUtils;
        }
    
        @Override
        public Account queryAccountByCardNo(String cardNo) throws Exception {
            //Connection connection = DruidUtils.getInstance().getConnection();
            Connection connection = connectionUtils.getCurrentThreadConn();
    
            String sql = "select * from account where cardNo = ?";
    
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, cardNo);
            ResultSet resultSet = preparedStatement.executeQuery();
    
            Account account = new Account();
            while (resultSet.next()){
                account.setCardNo(resultSet.getString("cardNo"));
                account.setName(resultSet.getString("name"));
                account.setMoney(resultSet.getInt("money"));
    
            }
    
            resultSet.close();
            preparedStatement.close();
            //connection.close();
    
            return account;
    
        }
    
        @Override
        public int updateAccountByCardNo(Account account) throws Exception {
    
            //Connection connection = DruidUtils.getInstance().getConnection();
            Connection connection = connectionUtils.getCurrentThreadConn();
    
            String sql = "update account set money = ? where cardNo = ?";
    
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, account.getMoney());
            preparedStatement.setString(2, account.getCardNo());
    
            int i = preparedStatement.executeUpdate();
    
            preparedStatement.close();
            //connection.close();
    
            return i;
        }
    }
    
    
  6. 修改beans.xml,添加ConnectionUtils、TransactionManager、ProxyFactory三个bean,并将ConnectionUtils的bean注入JdbcAccountDaoImpl

    <?xml version="1.0" encoding="UTF-8" ?>
    <beans>
    
        <bean id = "transferService" class = "com.lb.service.impl.TransferServiceImpl">
            <property name = "AccountDao" ref = "accountDao"></property>
    
        </bean>
    
        <bean id = "accountDao" class="com.lb.dao.impl.JdbcAccountDaoImpl">
            <property name="ConnectionUtils" ref="connectionUtils"></property>
        </bean>
    
    
        <!--三个新增bean-->
        <bean id="connectionUtils" class="com.lb.utils.ConnectionUtils"></bean>
    
        <bean id="transactionManager" class="com.lb.utils.TransactionManager">
            <property name="ConnectionUtils" ref="connectionUtils"></property>
        </bean>
    
        <bean id="proxyFactory" class="com.lb.factory.ProxyFactory">
            <property name="TransactionManager" ref="transactionManager"></property>
        </bean>
    </beans>
    

三、使用Spring实现IOC和AOP的案例改造

1. IOC基础

1.1 BeanFactory和ApplicationContext的区别

  • BeanFactory是Spring框架中IoC容器的顶层接口,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,而ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。

  • 通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接口,比 BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等。

请添加图片描述

1.2 基础使用

  1. 引入spring的jar包依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.12.RELEASE</version>
    </dependency>
    
  2. 添加applicationContext.xml,配置bean的三种方式:

    • 纯xml配置

      1. 添加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
         https://www.springframework.org/schema/beans/spring-beans.xsd">
        
      2. 启动IOC容器的方式:

        • javaSE启动IOC容器,通过ClassPathXmlApplicationContext

          ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
          
        • web环境下启动IOC容器,在web.xml中配置监听器,当监听器启动时,classpath下applicationContext.xml会被加载,从而获取对应的bean存入容器

          从xml下启动:

          <!DOCTYPE web-app PUBLIC
          "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
          "http://java.sun.com/dtd/web-app_2_3.dtd" >
          <web-app>
           <display-name>Archetype Created Web Application</display-name>
           <!--配置Spring ioc容器的配置⽂件-->
           <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:applicationContext.xml</param-value>
           </context-param>
           <!--使⽤监听器启动Spring的IOC容器-->
           <listener>
           <listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
           </listener>
          </web-app>
          

          从配置类下启动:

          <!DOCTYPE web-app PUBLIC
          "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
          "http://java.sun.com/dtd/web-app_2_3.dtd" >
          <web-app>
           <display-name>Archetype Created Web Application</display-name>
           <!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
           <context-param>
           <param-name>contextClass</param-name>
           <paramvalue>org.springframework.web.context.support.AnnotationConfigWebAppli
          cationContext</param-value>
           </context-param>
              <!--配置启动类的全限定类名-->
           <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>com.lagou.edu.SpringConfig</param-value>
           </context-param>
           <!--使⽤监听器启动Spring的IOC容器-->
           <listener>
           <listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
           </listener>
          </web-app>
          
      3. 对象实例化:

        • 使用无参构造函数创建对象,只需要添加对应bean和id、class信息

          添加DemoBean类:

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

          applicationContext.xml中添加bean标签:

          <?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
           https://www.springframework.org/schema/beans/spring-beans.xsd">
          
              <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>
          
          
          </beans> 
          
        • 使用静态方法创建,创建工厂类的bean,添加生产bean对象的静态方法,在工厂bean配置中添加factory-method配置生产bean的静态方法

          添加生产bean的工厂类:

          public class CreateBeanFactory {
          
              public static DemoBean getInstance(){
                  return new DemoBean();
              }
          }
          

          applicationContext.xml添加工厂bean的配置信息:

          <?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
           https://www.springframework.org/schema/beans/spring-beans.xsd">
          
          <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
          
              <!--静态方法获取bean-->
              <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>
          
          
          </beans>
          

          应用场景:

          在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了 ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤ 此种⽅式配置。

        • 使用实例化方法创建,用于非静态方法创建bean对象,由于是非静态方法,需要先创建生产bean的对象,然后再创建bean

          CreateBeanFactory类添加普通生产bean方法:

          public class CreateBeanFactory {
          
              public static DemoBean getInstance(){
                  return new DemoBean();
              }
          
              public DemoBean getInstance2(){
                  return new DemoBean();
              }
          }
          

          修改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
           https://www.springframework.org/schema/beans/spring-beans.xsd">
          
          <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
          
              <!--静态方法获取bean-->
             <!-- <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>-->
          
              <!--实例化工厂类,然后创建对应的bean-->
              <bean id="createBeanFactory" class="com.lb.factory.CreateBeanFactory"></bean>
              <bean id="demoBean" factory-bean="createBeanFactory" factory-method="getInstance2"></bean>
          
          </beans>
          

          应用场景:

          在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是非静态⽅法,当是非静态方法时,即可 采⽤此配置方式。

      4. bean对象的其他标签属性:

        • scope:singleton单例(注入IOC容器,生命周期由容器管理),prototype(多例,生命周期不受容器管理)

          创建SingletonBean类,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
           https://www.springframework.org/schema/beans/spring-beans.xsd">
          
          <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
          
              <!--静态方法获取bean-->
             <!-- <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>-->
          
              <!--实例化工厂类,然后创建对应的bean-->
              <bean id="createBeanFactory" class="com.lb.factory.CreateBeanFactory"></bean>
              <bean id="demoBean" factory-bean="createBeanFactory" factory-method="getInstance2"></bean>
              
              <bean id="singletonBean" class="com.lb.pojo.SingletonBean" scope="singleton"/>
          
          </beans>
          
        • init-method/destory-method:bean的初始化时 和 销毁时 执行的方法,需注意只有scope为singleton时才会执行destory属性指定的方法

          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
           https://www.springframework.org/schema/beans/spring-beans.xsd">
          
          <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
          
              <!--静态方法获取bean-->
             <!-- <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>-->
          
              <!--实例化工厂类,然后创建对应的bean-->
              <bean id="createBeanFactory" class="com.lb.factory.CreateBeanFactory"></bean>
              <bean id="demoBean" factory-bean="createBeanFactory" factory-method="getInstance2"></bean>
          
              <bean id="singletonBean" class="com.lb.pojo.SingletonBean" scope="singleton" init-method="init" destroy-method="destory"/>
          
          </beans>
          

          测试代码:

          public class IocTest {
          
          
              @Test
              public void test(){
                  ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
          
          
                  DemoBean demoBean = (DemoBean) applicationContext.getBean("demoBean");
                  System.out.println(demoBean);
                  applicationContext.close();
              }
          }
          

          测试结果:

          请添加图片描述

      5. bean对象的依赖注入方式

        • 构造函数注入

          1. 创建bean对象,生成带参构造函数

            public class ConstructBean {
                
                private int id;
                
                private String name;
                
                private DemoBean demoBean;
            
                public ConstructBean(int id, String name, DemoBean demoBean) {
                    this.id = id;
                    this.name = name;
                    this.demoBean = demoBean;
                }
            
                @Override
                public String toString() {
                    return "ConstructBean{" +
                            "id=" + id +
                            ", name='" + name + '\'' +
                            ", demoBean=" + demoBean +
                            '}';
                }
            }
            
          2. 在xml文件中对应的bean配置内添加constructor-arg配置,name为set方法后的参数名,ref为当注入是bean对象时所对应的唯一id,value为注入的基本数据类型或String的值

            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
             https://www.springframework.org/schema/beans/spring-beans.xsd">
            
            <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
            
                <!--静态方法获取bean-->
               <!-- <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>-->
            
                <!--实例化工厂类,然后创建对应的bean-->
                <bean id="createBeanFactory" class="com.lb.factory.CreateBeanFactory"></bean>
                <bean id="demoBean" factory-bean="createBeanFactory" factory-method="getInstance2"></bean>
            
                <bean id="singletonBean" class="com.lb.pojo.SingletonBean" scope="singleton" init-method="init" destroy-method="destory"/>
            
                <!--构造函数注入属性-->
                <bean id="constructBean" class="com.lb.pojo.ConstructBean">
                    <constructor-arg name="id" value="1"/>
                    <constructor-arg name="name" value="zhangsan"/>
                    <constructor-arg name="demoBean" ref="demoBean"/>
                </bean>
            
            </beans>
            

            测试结果:

            请添加图片描述

        • set方法注入

          1. 在xml文件中对应的bean配置内添加property配置,name为set方法后的参数名,ref为当注入是bean对象时所对应的唯一id,value为注入的基本数据类型或String的值

            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
             https://www.springframework.org/schema/beans/spring-beans.xsd">
            
            <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
            
                <!--静态方法获取bean-->
               <!-- <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>-->
            
                <!--实例化工厂类,然后创建对应的bean-->
                <bean id="createBeanFactory" class="com.lb.factory.CreateBeanFactory"></bean>
                <bean id="demoBean" factory-bean="createBeanFactory" factory-method="getInstance2"></bean>
            
                <bean id="singletonBean" class="com.lb.pojo.SingletonBean" scope="singleton" init-method="init" destroy-method="destory"/>
            
                <!--构造函数注入属性-->
                <bean id="constructBean" class="com.lb.pojo.ConstructBean">
                    <constructor-arg name="id" value="1"/>
                    <constructor-arg name="name" value="zhangsan"/>
                    <constructor-arg name="demoBean" ref="demoBean"/>
                </bean>
            
                <!--set方法注入属性-->
                <bean id="setBean" class="com.lb.pojo.SetBean">
                    <property name="id" value="2"></property>
                    <property name="name" value="lisi"></property>
                    <property name="demoBean" ref="demoBean"></property>
                </bean>
            
            </beans>
            

            测试结果:

            请添加图片描述

          2. 注入复杂数据类型 --> 集合 类型,包括List 和 Map两种

            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
             https://www.springframework.org/schema/beans/spring-beans.xsd">
            
            <!--    <bean id="demoBean" class="com.lb.pojo.DemoBean"></bean>-->
            
                <!--静态方法获取bean-->
               <!-- <bean id="demoBean" class="com.lb.factory.CreateBeanFactory" factory-method="getInstance"></bean>-->
            
                <!--实例化工厂类,然后创建对应的bean-->
                <bean id="createBeanFactory" class="com.lb.factory.CreateBeanFactory"></bean>
                <bean id="demoBean" factory-bean="createBeanFactory" factory-method="getInstance2"></bean>
            
                <bean id="singletonBean" class="com.lb.pojo.SingletonBean" scope="singleton" init-method="init" destroy-method="destory"/>
            
                <!--构造函数注入属性-->
                <bean id="constructBean" class="com.lb.pojo.ConstructBean">
                    <constructor-arg name="id" value="1"/>
                    <constructor-arg name="name" value="zhangsan"/>
                    <constructor-arg name="demoBean" ref="demoBean"/>
                </bean>
            
                <!--set方法注入属性-->
                <bean id="setBean" class="com.lb.pojo.SetBean">
                    <property name="id" value="2"></property>
                    <property name="name" value="lisi"></property>
                    <property name="demoBean" ref="demoBean"></property>
                </bean>
            
                <!--复杂数据类型注入-->
                <bean id="complexDataBean" class="com.lb.pojo.ComplexDataBean">
                    <property name="list">
                        <array>
                            <value>1</value>
                            <value>2</value>
                            <value>3</value>
                        </array>
                    </property>
            
                    <property name="set">
                        <set>
                            <value>set1</value>
                            <value>set2</value>
                            <value>set3</value>
                        </set>
                    </property>
            
                    <property name="map">
                        <map>
                            <entry key="key1" value="1"></entry>
                            <entry key="key2" value="2"></entry>
                        </map>
                    </property>
            
                    <property name="properties">
                        <props>
                            <prop key="prop1">value1</prop>
                            <prop key="prop2">value2</prop>
                        </props>
                    </property>
                </bean>
            
            </beans>
            

            测试:

            请添加图片描述

    • xml + 注解(springIOC容器的启动仍从加载xml开始;第三方jar中的bean定义在xml,比如德鲁伊数据库连接池,自定定义的bean用注解)

      1. xml中标签与注解的对应关系;

        xml标签对应注解形式
        bean标签@Component(“accountDao”),注解加在类上 bean的id属性内容直接配置在注解后⾯如果不配置,默认定义个这个bean的id为类的类名首字母小写; 另外,针对分层代码开发提供了@Componenet的三种别名@Controller、 @Service、@Repository分别⽤于控制层类、服务层类、dao层类的bean定义,这 四个注解的用法完全⼀样,只是为了更清晰的区分⽽已
        标签的 scope属 性@Scope(“prototype”),默认单例,注解加在类上
        标签的 initmethod 属性@PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法
        标签的 destorymethod 属性@PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法

        创建一个使用注解的类:

        @Component("demoBean1")
        @Scope("singleton")
        public class AnnoDemoBean {
        
            @PostConstruct
            public void init(){
                System.out.println("PostConstuct...");
            }
            
            @PreDestroy
            public void destory(){
                System.out.println("PreDestory...");
            }
        }
        

        测试代码:

        AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext("com.lb");
        
        DemoBean demoBean = (DemoBean) applicationContext1.getBean("demoBean");
        System.out.println(demoBean);
        applicationContext1.close();
        

        测试结果:

        请添加图片描述

        创建一个多例对象,测试每次生成实例是否为同一个。

        多例类代码:

        @Component("prototypeBean")
        @Scope("prototype")
        public class PrototypeBean {
        }
        

        测试代码:

        PrototypeBean prototypeBean1 = (PrototypeBean) applicationContext1.getBean("prototypeBean");
        System.out.println(prototypeBean1);
        PrototypeBean prototypeBean2 = (PrototypeBean) applicationContext1.getBean("prototypeBean");
        System.out.println(prototypeBean2);
        

        测试结果:

        请添加图片描述

      2. 依赖注入的实现方式1:@AutoWired --> 按照类型注入,若同一类型有多个bean值时,需要配合@Qualifier指定id来告诉Spring具体装配哪个对象。

        创建接口类及其实现类

        public interface FlyAble {
        
            public void fly();
        }
        
        @Component
        public class Bird implements FlyAble {
            @Override
            public void fly() {
                System.out.println("鸟在天空飞...");
            }
        }
        
        @Component
        public class Plane implements FlyAble {
            @Override
            public void fly() {
                System.out.println("飞机在天空飞...");
            }
        }
        

        创建demo类自动注入接口类,当不指定具体哪个实现时:

        @Component
        public class AutoWiredDemo {
        
            @Autowired
            public FlyAble flyAble;
        
        }
        

        测试结果

        请添加图片描述

        当指定具体实现时:

        @Component
        public class AutoWiredDemo {
        
            @Autowired
            @Qualifier("plane")
            public FlyAble flyAble;
        
        }
        

        测试代码:

        AutoWiredDemo autoWiredDemo = (AutoWiredDemo) applicationContext1.getBean("autoWiredDemo");
        autoWiredDemo.flyAble.fly();
        

        测试结果:

        请添加图片描述

      3. 依赖注入的实现方式2:@Resource (在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包

        • 如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不 到则抛出异常。
        • 如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异 常。
        • 如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个, 都会抛出异常。
        • 如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配。

        测试类代码:

        @Component
        public class ResourceDemo {
        
            @Resource(name = "bird", type = FlyAble.class)
            public FlyAble flyAble;
        }
        

        测试代码:

        ResourceDemo resourceDemo = (ResourceDemo) applicationContext1.getBean("resourceDemo");
        resourceDemo.flyAble.fly();
        

        测试结果:

        请添加图片描述

    • 纯注解方式

      1. 从配置类启动容器,在web中需要使用监听器启动IOC容器,告诉监听器使用注解方式启动IOC容器,配置启动类的全限定类名
      2. 根据注解 和 xml中标签的对应关系,去除applicationContext.xml文件,改为注解配置

2. 案例改造

引入spring的jar包依赖

<!--引入Spring IoC容器功能-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.1.12.RELEASE</version>
</dependency>
<!--引入spring web功能-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.1.12.RELEASE</version>
</dependency>

2.1 xml改造

  • 去除BeanFactory类,所有bean从Spring容器中获取

  • 改造web.xml,使用监听器启动IOC容器

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
    
      <!--配置SpringIoc容器的配置文件-->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
      
      <!--监听器启动IOC容器-->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    </web-app>
    
  • 将beans.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"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
    ">
    
        <bean id = "transferService" class = "com.lb.service.impl.TransferServiceImpl">
            <property name = "AccountDao" ref = "accountDao"></property>
    
        </bean>
    
        <bean id = "accountDao" class="com.lb.dao.impl.JdbcAccountDaoImpl">
            <property name="ConnectionUtils" ref="connectionUtils"></property>
        </bean>
    
    
        <!--三个新增bean-->
        <bean id="connectionUtils" class="com.lb.utils.ConnectionUtils"></bean>
    
        <bean id="transactionManager" class="com.lb.utils.TransactionManager">
            <property name="ConnectionUtils" ref="connectionUtils"></property>
        </bean>
    
        <bean id="proxyFactory" class="com.lb.factory.ProxyFactory">
            <property name="TransactionManager" ref="transactionManager"></property>
        </bean>
    </beans>
    
  • 修改TransferServlet,重写init方法,web容器启动时,init方法会执行一次,此时可以从spring容器中获取TransferService实例。

@WebServlet(name = "transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {

    //private TransferService transferService = new TransferServiceImpl();
    //private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");

/*    private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
    private TransferService transferService = (TransferService) proxyFactory.getProxy(BeanFactory.getBean("transferService"));*/

    private TransferService transferService;

    @Override
    public void init() throws ServletException {
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        transferService = (TransferService) webApplicationContext.getBean("transferService");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");

        String fromCardNo = req.getParameter("fromCardNo");
        String toCardNo = req.getParameter("toCardNo");
        String moneyStr = req.getParameter("money");
        int money = Integer.parseInt(moneyStr);

        Result result = new Result();
        try {
            transferService.transfer(fromCardNo, toCardNo, money);
            result.setStatus("200");
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus("201");
            result.setMessage(e.toString());
        }

        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().print(JsonUtils.object2Json(result));

    }
}

2.2 xml + 注解改造 (在纯xml配置的基础上做修改,第三方jar包中定义的bean使用xml,自定义bean使用注解形式)

  • 开启注解扫描,扫描指定包下是否有springbean需要生成

        <!--开启注解扫描,base-package指定扫描的包路径-->
        <context:component-scan base-package="com.lb"/>
    
  • 第三方jar包的bean只有Druid连接池,需要引入外部资源文件,定义数据库的相关属性,再将第三方jar中的bean定义在xml中

        <!--引入外部资源文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--第三方jar中的bean定义在xml中-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
  • 对应applicationContext.xml中自定义bean,添加注解

2.3 纯注解改造 (在xml + 注解 的工程上改造)

  • 对应applicationContext.xml,创建配置类,配置第三方bean,然后删除applicationContext.xml

    @Configuration
    @ComponentScan("com.lb")
    @PropertySource({"classpath:jdbc.properties"})
    public class DataSourceConfig {
    
        @Value("${jdbc.driver}")
        private String driverClassName;
    
        @Value("${jdbc.url}")
        private String url;
    
        @Value("${jdbc.username}")
        private String username;
    
        @Value("${jdbc.password}")
        private String password;
    
        @Bean("dataSource")
        public DataSource dataSource(){
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driverClassName);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    
    }
    
  • 修改web.xml,告诉监听器使用注解的方式启动IOC容器

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
    
      <!--告诉ContextloaderListener知道我们使用注解的方式启动ioc容器-->
      <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
      </context-param>
      
      <!--配置启动类的全限定类名-->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.lb.config.DataSourceConfig</param-value>
      </context-param>
      
      <!--监听器启动IOC容器-->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    </web-app>
    
    
    
    
    

3. AOP基础

3.1 Aop基本介绍

  • 在SpringAop中包含切入点,方位点和横切逻辑,此三部分可以锁定在哪个地方插入什么横切逻辑代码。

    在xml配置文件中的对应关系

    请添加图片描述

  • 关于切入点表达式的说明(指横切逻辑需要插入的具体哪个方法)

    表达式语法说明

    示例:public * com.lb.dao.impl.UserDaoImpl.findUserById(java.lang.Integer))

访问修饰符返回值包名类名和方法名参数列表
可省略返回值可以使⽤*,表示任意返回值1、 包名可以使⽤.表示任意包,但是有⼏级包,必须写⼏个点 2、 包名可以使⽤…表示当前包及其⼦包类名和⽅法名,都可以使⽤.表示任意类,任意⽅法参数列表,可以使⽤具体类型 基本类型直接写类型名称 : int 引⽤类型必须写全限定类名:java.lang.String 参数列表可以使⽤*,表示任意参数类型,但是必须有参数 参数还可以使用…,表示有无参数均可,有参数可以是任意类型
  • 关于SpringAop的代理方式

    Spring 实现AOP思想使⽤的是动态代理技术。默认情况下,Spring会根据被代理对象是否实现接口来选择使⽤JDK还是CGLIB。当被代理对象没有实现 任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过 我们可以通过配置的⽅式,让Spring强制使用CGLIB。

3.2 基础使用

  1. 引入aop相关jar包

  2. 纯xml实现AOP,创建测试相关代码

    • pojo类 及 查询User接口和查询实现类

      User类

      public class User {
      
          private int id;
      }
      

      IUserDao接口

      public interface IUserDao {
      
          public User findUserById(Integer id);
      }
      

      UserDaoImpl实现类

      public class UserDaoImpl implements IUserDao {
          @Override
          public User findUserById(Integer id) {
      
              System.out.println("查找中...");
              //int i = 1/0;
              return new User();
          }
      }
      
    • 横切逻辑类LogUtils

      public class LogUtils {
      
          public void printLogBef(JoinPoint joinPoint){
              Object[] args = joinPoint.getArgs();
              System.out.println("前置通知,参数是:" + Arrays.toString(args));
          }
      
          public void printLogReturning(Object returnValue){
              System.out.println("正常返回通知,返回值是:" + returnValue);
          }
      
          public void printLogException(Throwable e){
              System.out.println("异常通知,异常是:" + e);
          }
      
          public void printLogFinally(){
              System.out.println("最终通知...");
          }
      
          //环绕通知可以随意在方法执行的任何时机定义横切逻辑
          public Object printLogAround(ProceedingJoinPoint proceedingJoinPoint){
      
              //定义返回值
              Object returnValue = null;
              try {
      
                  System.out.println("前置通知...");
      
                  //1、获取参数
                  Object[] args = proceedingJoinPoint.getArgs();
                  //2、执行切入点方法
                  returnValue = proceedingJoinPoint.proceed();
      
                  System.out.println("后置通知...");
      
              }catch (Throwable t){
                  //异常通知
                  System.out.println("异常通知");
                  t.printStackTrace();
              }finally {
                  //最终通知
                  System.out.println("最终通知");
              }
      
              return returnValue;
      
          }
      }
      
    • applicationContext.xml配置文件编写

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
              http://www.springframework.org/schema/beans
              https://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="userDao" class="com.lb.dao.impl.UserDaoImpl"></bean>
      
          <!--注入横切逻辑类的bean-->
          <bean id="logUtils" class="com.lb.utils.LogUtils"></bean>
      
          <!--aop配置开始-->
          <aop:config>
              <!--配置横切逻辑类-->
              <aop:aspect id="logAdvice" ref="logUtils">
                  <!--配置前置通知-->
                  <aop:pointcut id="pt" expression="execution(public * com.lb.dao.impl.UserDaoImpl.findUserById(java.lang.Integer))"/>
                  <aop:before method="printLogBef" pointcut-ref="pt"></aop:before>
      
                  <!--配置正常返回通知-->
                  <aop:after-returning method="printLogReturning" returning="returnValue" pointcut-ref="pt"></aop:after-returning>
      
                  <!--配置异常通知-->
                  <aop:after-throwing method="printLogException" throwing="e" pointcut-ref="pt"></aop:after-throwing>
      
                  <!--配置返回前通知-->
                  <aop:after method="printLogFinally" pointcut-ref="pt"></aop:after>
      
                  <!--配置环绕通知-->
                  <!--<aop:around method="printLogAround" pointcut-ref="pt"></aop:around>-->
      
              </aop:aspect>
      
          </aop:config>
      
      
      </beans>
      
    • 测试代码

      public class Aoptest {
      
      
          @Test
          public void test(){
              ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
              IUserDao userDao = (IUserDao) applicationContext.getBean("userDao");
              userDao.findUserById(1);
          }
      }
      
    • 正常测试

      请添加图片描述

    • 异常测试

      请添加图片描述

    • 环绕通知测试

      请添加图片描述

    spring在纯xml模式下配置强制使用cglib动态代理的方式(需导入cglib的jar包)

    <aop:config proxy-target-class="true">
    
  3. xml + 注解 实现AOP,修改相关代码

    • 对应关系

      请添加图片描述

    • 在IUserDao中添加方法

      public User findUserByIdXmlAnno(Integer id);
      
    • 在UserDaoImpl中添加方法

      @Override
      public User findUserByIdXmlAnno(Integer id) {
          System.out.println("查找中...");
          return new User();
      }
      
    • 添加横切逻辑类

      @Component
      @Aspect
      public class LogUtilsXmlAnno {
      
          @Pointcut("execution(* com.lb.dao.impl.UserDaoImpl.findUserByIdXmlAnno(..))")
          public void pointcut(){}
      
          @Before("pointcut()")
          public void printLogBef(JoinPoint joinPoint){
              Object[] args = joinPoint.getArgs();
              System.out.println("前置通知,参数是:" + Arrays.toString(args));
          }
      
          @AfterReturning(value = "pointcut()", returning = "returnValue")
          public void printLogReturning(Object returnValue){
              System.out.println("正常返回通知,返回值是:" + returnValue);
          }
      
          @AfterThrowing(value = "pointcut()", throwing = "e")
          public void printLogException(Throwable e){
              System.out.println("异常通知,异常是:" + e);
          }
      
          @After("pointcut()")
          public void printLogFinally(){
              System.out.println("最终通知...");
          }
      
          //环绕通知可以随意在方法执行的任何时机定义横切逻辑
          @Around("pointcut()")
          public Object printLogAround(ProceedingJoinPoint proceedingJoinPoint){
      
              //定义返回值
              Object returnValue = null;
              try {
      
                  System.out.println("前置通知...");
      
                  //1、获取参数
                  Object[] args = proceedingJoinPoint.getArgs();
                  //2、执行切入点方法
                  returnValue = proceedingJoinPoint.proceed();
      
                  System.out.println("后置通知...");
      
              }catch (Throwable t){
                  //异常通知
                  System.out.println("异常通知");
                  t.printStackTrace();
              }finally {
                  //最终通知
                  System.out.println("最终通知");
              }
      
              return returnValue;
      
          }
      }
      
    • XML 中开启 Spring 对注解 AOP 的⽀持,开启包扫描 ,注释掉其他配置,将UserDao用@Component注解标注

      <!--开启spring对注解aop的⽀持-->
      <aop:aspectj-autoproxy/>
      
      <context:component-scan base-package="com.lb"/>
      
    • 测试代码:

      @Test
      public void test2(){
          ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
          IUserDao userDao = (IUserDao) applicationContext.getBean("userDao");
          userDao.findUserByIdXmlAnno(1);
      }
      
    • 测试结果:

      请添加图片描述

      spring在xml+注解方式下配置强制使用cglib动态代理的方式(需导入cglib的jar包)

      <aop:aspectj-autoproxy proxy-target-class="true"/>
      
  4. 纯注解 实现AOP,修改相关代码

    使用纯注解开发,需要用注解替换掉applicationContext.xml中的注解支持的配置。

    注释xml文件,创建SpringConfiguration 配置类

    @Configuration
    @ComponentScan("com.lagou")
    @EnableAspectJAutoProxy //开启spring对注解AOP的⽀持
    public class SpringConfiguration {
    }
    

    测试代码:

        @Test
        public void test3(){
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            IUserDao userDao = (IUserDao) applicationContext.getBean("userDao");
            userDao.findUserByIdXmlAnno(1);
        }
    

    测试结果:

    请添加图片描述

spring在纯注解方式下配置强制使用cglib动态代理的方式(需导入cglib的jar包)

在配置类上@EnableAspectJAutoProxy注解后标注

@EnableAspectJAutoProxy(proxyTargetClass = true)

4. spring声明式事务

4.1 事务概念

事务指逻辑上的⼀组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。从⽽确保了数 据的准确与安全。

编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务

声明式事务:通过xml或者注解配置的⽅式达到事务控制的目的,叫做声明式事务

4.2 事务的四大特性

  • 原子性(Atomicity):事务是一个不可分割的工作单位,事务中操作要么都发生,要么都不发生。

  • 一致性(Consistency):事务必须使数据库从⼀个⼀致性状态变换到另外⼀个⼀致性状态

    例如转账前A有1000,B有1000。转账后A+B也得是2000。

  • 隔离性(Isolation): 事务的隔离性是多个⽤户并发访问数据库时,数据库为每⼀个⽤户开启的事务, 每个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

    比如:事务1给员⼯涨⼯资2000,但是事务1尚未被提交,员⼯发起事务2查询⼯资,发现⼯资涨了2000 块钱,读到了事务1尚未提交的数据(脏读)

  • 持久性(Durability) :指⼀个事务⼀旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发⽣故障 也不应该对其有任何影响。

4.3 事务的隔离级别

错误情况

  • 脏读:⼀个线程中的事务读到了另外⼀个线程中未提交的数据。
  • 不可重复读:⼀个线程中的事务读到了另外⼀个线程中已经提交的update的数据(前后内容不⼀样)
  • 虚读(幻读):⼀个线程中的事务读到了另外⼀个线程中已经提交的insert或者delete的数据(前后条数不⼀样)

对应的隔离级别

  • Serializable(串⾏化): 都可避免
  • Repeatable read(可重复读):可避免脏读、不可重复读情况的发⽣。
  • Read committed(读已提交):可避免脏读情况发⽣。
  • Read uncommitted(读未提交): 所有情况都无法避免

级别越高,效率越低

4.4 事务的传播行为

事务往往在service层进⾏控制,如果出现service层⽅法A调⽤了另外⼀个service层⽅法B,A和B⽅法本 身都已经被添加了事务控制,那么A调⽤B的时候,就需要进⾏事务的⼀些协商,这就叫做事务的传播行为。

A调⽤B,我们站在B的⻆度来观察来定义事务的传播⾏为

PROPAGATION_REQUIRED如果当前没有事务,就新建⼀个事务,如果已经存在⼀个事务中, 加⼊到这个事务中。这是最常⻅的选择。
PROPAGATION_SUPPORTS⽀持当前事务,如果当前没有事务,就以⾮事务⽅式执⾏。
PROPAGATION_MANDATORY使⽤当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执⾏。如果当前没有事务,则 执⾏与PROPAGATION_REQUIRED类似的操作。

4.5 spring声明式事务是什么

声明式事务要做的就是使⽤Aop(动态代 理)来将事务控制逻辑织⼊到业务代码

Spring不实现事务,只定义了事务实现的接口,具体实现由相应的jar包负责。

如下为Spring提供的事务接口:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;

    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;

    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

举例:

  • Spring有内置的一些具体策略:DataSourceTransactionManager , HibernateTransactionManager 等 等。
  • 其他的策略:Spring JdbcTemplate(数据库操作⼯具)、Mybatis(mybatis-spring.jar)–> DataSourceTransactionManager

5. 案例改造(添加spring事务管理)

添加pom依赖

<!--spring aop的jar包支持-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.12.RELEASE</version>
</dependency>

<!--第三方的aop框架aspectj的jar-->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.13</version>
</dependency>

<!--引入spring声明式事务相关-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.1.12.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.1.12.RELEASE</version>
</dependency>

5.1纯xml改造(在springioc的实现案例上改造)

  1. 删除ConnectionUtils、TransactionManager、ProxyFactory三个自定义事务相关的类

  2. 删除applicationContext.xml中相关的bean注入

  3. 从DruidUtils类中直接获取数据源dataSource的bean,使用静态方法获取

    <bean id="dataSource" class="com.lb.utils.DruidUtils" factory-method="getInstance"></bean>
    
  4. 将accountDao中对ConnectionUtils的依赖注入 改为 直接注入dataSource

    <bean id = "accountDao" class="com.lb.dao.impl.JdbcAccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
  5. 修改JdbcAccountDaoImpl类代码,改为直接注入dataSource

    public class JdbcAccountDaoImpl implements AccountDao {
        private DataSource dataSource;
    
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        @Override
        public Account queryAccountByCardNo(String cardNo) throws Exception {
            Connection connection = dataSource.getConnection();
    
            String sql = "select * from account where cardNo = ?";
    
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, cardNo);
            ResultSet resultSet = preparedStatement.executeQuery();
    
            Account account = new Account();
            while (resultSet.next()){
                account.setCardNo(resultSet.getString("cardNo"));
                account.setName(resultSet.getString("name"));
                account.setMoney(resultSet.getInt("money"));
    
            }
    
            resultSet.close();
            preparedStatement.close();
    
            return account;
    
        }
    
        @Override
        public int updateAccountByCardNo(Account account) throws Exception {
    
            //Connection connection = DruidUtils.getInstance().getConnection();
            Connection connection = dataSource.getConnection();
    
            String sql = "update account set money = ? where cardNo = ?";
    
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, account.getMoney());
            preparedStatement.setString(2, account.getCardNo());
    
            int i = preparedStatement.executeUpdate();
    
            preparedStatement.close();
            //connection.close();
    
            return i;
        }
    }
    
  6. applicationContext.xml中配置横切逻辑类(PlatformTransactionManager接口的实现类)、事务属性定义、然后配置aop属性,将事务的横切逻辑织入Service代码中做事务管理

    <!--定义横切逻辑类-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
    
    <!--事务属性定义-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--定制事务细节,传播行为、隔离级别等-->
        <tx:attributes>
            <!--一般性配置-->
            <tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
            <!--针对查询的覆盖性配置-->
            <tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
    
        </tx:attributes>
    
    </tx:advice>
    
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lb.service.impl.TransferServiceImpl.*(..))"/>
    </aop:config>
    

5.2 xml + 注解改造

  1. 在applicationContext.xml添加对注解事务的支持

    <!--配置事务管理器的具体实现(横切逻辑类)-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!--开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
  2. 在接口、类或者方法上添加@Transactional注解

@Transactional(readOnly = false, propagation = Propagation.REQUIRED)

5.3 纯注解改造

Spring基于注解驱动开发的事务控制配置,只需要把 xml 配置部分改为注解实现。只是需要⼀个注解替换掉xml配置⽂件中的配置。

在 Spring 的配置类上添加 @EnableTransactionManagement 注解即可

@EnableTransactionManagement//开启spring注解事务的⽀持
public class SpringConfiguration {
}

总结

  • Spring框架的核心思想就是IOC和AOP,主要解决了工程的对象创建和管理问题、横切逻辑代码重复问题。
  • 本文从Spring的定义、基本特性、核心思想、简单案例实现、自定义IOC和AOP,再到Spring 的IOC和AOP使用介绍,最后到使用Spring进行案例改造,逐步推进,由浅入深,完成对Spring框架的学习。
  • 关于spring的延迟加载、FactoryBean,可以查看下面两个博客:
    spring的延迟加载介绍
    spring关于FactoryBean的基本介绍
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值