{"id":15196860,"url":"https://github.com/mozhu811/learning-spring","last_synced_at":"2025-10-28T04:31:31.045Z","repository":{"id":36352123,"uuid":"176844161","full_name":"mozhu811/learning-spring","owner":"mozhu811","description":"Spring学习笔记","archived":false,"fork":false,"pushed_at":"2022-12-16T03:22:51.000Z","size":91,"stargazers_count":151,"open_issues_count":3,"forks_count":17,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-01T11:24:46.516Z","etag":null,"topics":["spring","spring-framework"],"latest_commit_sha":null,"homepage":"https://cruii.io/archives/2019111907160329543","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mozhu811.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-21T01:20:12.000Z","updated_at":"2024-10-28T15:55:07.000Z","dependencies_parsed_at":"2023-01-17T00:43:27.751Z","dependency_job_id":null,"html_url":"https://github.com/mozhu811/learning-spring","commit_stats":null,"previous_names":["mozhu811/learning-spring","cruii/learning-spring"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-spring","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-spring/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-spring/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-spring/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mozhu811","download_url":"https://codeload.github.com/mozhu811/learning-spring/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238597386,"owners_count":19498396,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["spring","spring-framework"],"created_at":"2024-09-28T00:05:04.564Z","updated_at":"2025-10-28T04:31:25.443Z","avatar_url":"https://github.com/mozhu811.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring 学习笔记\n\n* [IOC 和 DI 的概述](#ioc-%E5%92%8C-di-%E7%9A%84%E6%A6%82%E8%BF%B0)\n  * [IOC(Inversion of Controll)](#iocinversion-of-controll)\n  * [DI(Dependency Injection)](#didependency-injection)\n* [Spring配置](#spring%E9%85%8D%E7%BD%AE)\n  * [ApplicationContext](#applicationcontext)\n  * [Bean的相关配置](#bean%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE)\n    * [bean标签的id和name的配置](#bean%E6%A0%87%E7%AD%BE%E7%9A%84id%E5%92%8Cname%E7%9A%84%E9%85%8D%E7%BD%AE)\n    * [bean的生命周期的配置](#bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E7%9A%84%E9%85%8D%E7%BD%AE)\n    * [bean的作用范围的配置](#bean%E7%9A%84%E4%BD%9C%E7%94%A8%E8%8C%83%E5%9B%B4%E7%9A%84%E9%85%8D%E7%BD%AE)\n  * [Spring的Bean管理配置](#spring%E7%9A%84bean%E7%AE%A1%E7%90%86%E9%85%8D%E7%BD%AE)\n    * [Spring的Bean的实例化方式](#spring%E7%9A%84bean%E7%9A%84%E5%AE%9E%E4%BE%8B%E5%8C%96%E6%96%B9%E5%BC%8F)\n      * [无参构造方式（默认）](#%E6%97%A0%E5%8F%82%E6%9E%84%E9%80%A0%E6%96%B9%E5%BC%8F%E9%BB%98%E8%AE%A4)\n      * [静态工厂实例化方式](#%E9%9D%99%E6%80%81%E5%B7%A5%E5%8E%82%E5%AE%9E%E4%BE%8B%E5%8C%96%E6%96%B9%E5%BC%8F)\n      * [实例工厂实例化方式](#%E5%AE%9E%E4%BE%8B%E5%B7%A5%E5%8E%82%E5%AE%9E%E4%BE%8B%E5%8C%96%E6%96%B9%E5%BC%8F)\n    * [Spring的属性注入方式](#spring%E7%9A%84%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5%E6%96%B9%E5%BC%8F)\n      * [构造方法方式的属性注入](#%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95%E6%96%B9%E5%BC%8F%E7%9A%84%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5)\n      * [Set方法方式的属性注入](#set%E6%96%B9%E6%B3%95%E6%96%B9%E5%BC%8F%E7%9A%84%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5)\n      * [为Bean注入引用类型的数据](#%E4%B8%BAbean%E6%B3%A8%E5%85%A5%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%95%B0%E6%8D%AE)\n      * [P名称空间的属性注入（Spring2\\.5）](#p%E5%90%8D%E7%A7%B0%E7%A9%BA%E9%97%B4%E7%9A%84%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5spring25)\n      * [SpEL方式的属性注入（Spring3）](#spel%E6%96%B9%E5%BC%8F%E7%9A%84%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5spring3)\n    * [注入集合类型的数据](#%E6%B3%A8%E5%85%A5%E9%9B%86%E5%90%88%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%95%B0%E6%8D%AE)\n    * [Spring分模块开发的配置](#spring%E5%88%86%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91%E7%9A%84%E9%85%8D%E7%BD%AE)\n* [Spring开发中的常用注解](#spring%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%94%A8%E6%B3%A8%E8%A7%A3)\n  * [@Component](#component)\n  * [@Value](#value)\n  * [@Autowired](#autowired)\n  * [@Resource](#resource)\n  * [@PostConstruct 和 @PreDestroy](#postconstruct-%E5%92%8C-predestroy)\n  * [@Scope](#scope)\n  * [基于XML配置和基于注解配置的对比](#%E5%9F%BA%E4%BA%8Exml%E9%85%8D%E7%BD%AE%E5%92%8C%E5%9F%BA%E4%BA%8E%E6%B3%A8%E8%A7%A3%E9%85%8D%E7%BD%AE%E7%9A%84%E5%AF%B9%E6%AF%94)\n* [Spring AOP](#spring-aop)\n  * [AOP的概述](#aop%E7%9A%84%E6%A6%82%E8%BF%B0)\n  * [AOP的案例（应用场景）](#aop%E7%9A%84%E6%A1%88%E4%BE%8B%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF)\n  * [AOP底层实现原理](#aop%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)\n    * [JDK动态代理](#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86)\n      * [JDK动态代理案例](#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E6%A1%88%E4%BE%8B)\n    * [Cglib动态代理](#cglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86)\n      * [Cglib动态代理案例](#cglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E6%A1%88%E4%BE%8B)\n  * [Spring中的AOP实现——AspectJ](#spring%E4%B8%AD%E7%9A%84aop%E5%AE%9E%E7%8E%B0aspectj)\n    * [AOP开发中的相关术语](#aop%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84%E7%9B%B8%E5%85%B3%E6%9C%AF%E8%AF%AD)\n    * [AspectJ的XML配置案例](#aspectj%E7%9A%84xml%E9%85%8D%E7%BD%AE%E6%A1%88%E4%BE%8B)\n    * [Spring中常用的增强类型](#spring%E4%B8%AD%E5%B8%B8%E7%94%A8%E7%9A%84%E5%A2%9E%E5%BC%BA%E7%B1%BB%E5%9E%8B)\n      * [前置增强](#%E5%89%8D%E7%BD%AE%E5%A2%9E%E5%BC%BA)\n      * [后置增强](#%E5%90%8E%E7%BD%AE%E5%A2%9E%E5%BC%BA)\n      * [环绕增强](#%E7%8E%AF%E7%BB%95%E5%A2%9E%E5%BC%BA)\n      * [异常抛出增强](#%E5%BC%82%E5%B8%B8%E6%8A%9B%E5%87%BA%E5%A2%9E%E5%BC%BA)\n      * [最终增强](#%E6%9C%80%E7%BB%88%E5%A2%9E%E5%BC%BA)\n    * [AOP切入点表达式语法](#aop%E5%88%87%E5%85%A5%E7%82%B9%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%AD%E6%B3%95)\n    * [AspectJ的注解配置案例](#aspectj%E7%9A%84%E6%B3%A8%E8%A7%A3%E9%85%8D%E7%BD%AE%E6%A1%88%E4%BE%8B)\n* [Spring JDBC Template](#spring-jdbc-template)\n  * [JDBC Template的入门](#jdbc-template%E7%9A%84%E5%85%A5%E9%97%A8)\n    * [基本使用](#%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8)\n    * [数据库连接池](#%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0)\n      * [DBCP连接池的配置](#dbcp%E8%BF%9E%E6%8E%A5%E6%B1%A0%E7%9A%84%E9%85%8D%E7%BD%AE)\n      * [C3P0连接池配置](#c3p0%E8%BF%9E%E6%8E%A5%E6%B1%A0%E9%85%8D%E7%BD%AE)\n    * [完成基本的CRUD操作](#%E5%AE%8C%E6%88%90%E5%9F%BA%E6%9C%AC%E7%9A%84crud%E6%93%8D%E4%BD%9C)\n      * [插入操作](#%E6%8F%92%E5%85%A5%E6%93%8D%E4%BD%9C)\n      * [修改操作](#%E4%BF%AE%E6%94%B9%E6%93%8D%E4%BD%9C)\n      * [删除操作](#%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C)\n      * [查询操作](#%E6%9F%A5%E8%AF%A2%E6%93%8D%E4%BD%9C)\n        * [查询某个属性](#%E6%9F%A5%E8%AF%A2%E6%9F%90%E4%B8%AA%E5%B1%9E%E6%80%A7)\n        * [查询返回单个对象](#%E6%9F%A5%E8%AF%A2%E8%BF%94%E5%9B%9E%E5%8D%95%E4%B8%AA%E5%AF%B9%E8%B1%A1)\n        * [查询返回对象集合](#%E6%9F%A5%E8%AF%A2%E8%BF%94%E5%9B%9E%E5%AF%B9%E8%B1%A1%E9%9B%86%E5%90%88)\n* [Spring事务管理](#spring%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86)\n  * [什么是事务](#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BA%8B%E5%8A%A1)\n  * [事务的特性](#%E4%BA%8B%E5%8A%A1%E7%9A%84%E7%89%B9%E6%80%A7)\n  * [不考虑隔离性引发的安全性问题](#%E4%B8%8D%E8%80%83%E8%99%91%E9%9A%94%E7%A6%BB%E6%80%A7%E5%BC%95%E5%8F%91%E7%9A%84%E5%AE%89%E5%85%A8%E6%80%A7%E9%97%AE%E9%A2%98)\n  * [解决读问题](#%E8%A7%A3%E5%86%B3%E8%AF%BB%E9%97%AE%E9%A2%98)\n  * [Spring事务管理API](#spring%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86api)\n  * [Spring事务的传播行为](#spring%E4%BA%8B%E5%8A%A1%E7%9A%84%E4%BC%A0%E6%92%AD%E8%A1%8C%E4%B8%BA)\n  * [Spring事务管理案例——转账情景](#spring%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86%E6%A1%88%E4%BE%8B%E8%BD%AC%E8%B4%A6%E6%83%85%E6%99%AF)\n    * [转账情景实现](#%E8%BD%AC%E8%B4%A6%E6%83%85%E6%99%AF%E5%AE%9E%E7%8E%B0)\n    * [编程式事务](#%E7%BC%96%E7%A8%8B%E5%BC%8F%E4%BA%8B%E5%8A%A1)\n    * [声明式事务](#%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1)\n      * [XML配置方式](#xml%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F)\n      * [注解配置方式](#%E6%B3%A8%E8%A7%A3%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F)\n\n# IOC 和 DI 的概述\n\n## IOC(Inversion of Controll)\n\n**思想是反转资源获取的方向**，传统的资源查找方式要求组件向容器发起请求查找资源。作为回应，容器适时的返回资源。而应用了IOC之后，则是**容器主动的将资源推送给它所管理的组件，组件所要做的仅是选择一种合适的方式来接收资源**\n\n## DI(Dependency Injection)\n\n是IOC的另一种表述方式，即**组件以一些预先定义好的方式(如：getter方法)来接收来自容器的资源注入**\n\n\u003c!-- more --\u003e\n\n# Spring配置\n\n在SpringIOC容器读取bean配置创建bean实例之前，必须对它进行实例化。只有在容器实例化后，才可以从IOC容器里获取bean实例并使用\n\nSpring提供了两种类型的IOC容器实现\n\n+ **BeanFactory：IOC容器的基本实现，在调用getBean()方法时才会实例化对象**\n+ **ApplicationContext：提供了更多的高级特性，在加载配置文件后就会实例化对象。是BeanFactory的子接口**\n\n`BeanFactory`是Spring框架的基础设施，面向Spring本身\n\n`ApplicationContext`面向使用Spring框架的开发者，几乎所有的应用场合都直接使用`ApplicationContext`而非底层的`BeanFactory`\n\n**无论使用何种方式，配置文件时都是相同的**\n\n```xml\n\u003c!-- 配置bean --\u003e\n\u003c!-- class: bean的全类名，通过反射的方式在IOC容器中创建bean，所以要求bean中必须有无参构造器 --\u003e\n\u003cbean id=\"people\" class=\"learningspring.ioc.examples.People\"\u003e\n    \u003cproperty name=\"name\" value=\"Chen\"/\u003e\n\u003c/bean\u003e\n```\n\n```java\npublic class People {\n    private String name;\n\n    public People() {\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String toString() {\n        return \"People{\" +\n                \"name='\" + name + '\\'' +\n                '}';\n    }\n}\n```\n\n```java\n@Test\npublic void test(){\n    // 创建IOC容器\n    ApplicationContext ctx = new FileSystemXmlApplicationContext(\"F:\\\\Projects\\\\IdeaProjects\\\\LearningSpring\\\\src\\\\main\\\\java\\\\learningspring\\\\ioc\\\\examples\\\\applicationContext.xml\");\n\n    // 从IOC容器中获取bean实例\n    People people = (People) ctx.getBean(\"people\");\n\n    System.out.println(people);\n}\n```\n\n## ApplicationContext\n\n`ApplicationContext`有两个实现类：\n\n+ `ClassPathXmlApplicationContext`：加载类路径里的配置文件\n+ `FileSystemXmlApplicationContext`：加载文件系统里的配置文件\n\n## Bean的相关配置\n\n### bean标签的id和name的配置\n\n+ `id`：使用了约束中的唯一约束。不能有特殊字符\n+ `name`：没有使用约束中的唯一约束（理论上可以重复，但是实际开发中不能出现）。可以有特殊字符\n\n### bean的生命周期的配置\n\n+ `init-method`：bean被初始化的时候执行的方法\n+ `destroy-method`：bean被销毁的时候执行的方法，前提是bean是单例的，工厂关闭\n\n### bean的作用范围的配置\n\n+ `scope`：bean的作用范围\n  + **singleton：单例模式，默认的作用域。**\n  + **prototype：多例模式。**\n  + request：应用在Web项目中，Spring创建这个类后，将这个类存入到request范围中。\n  + session：应用在Web项目中，Spring创建这个类后，将这个类存入到session范围中。\n  + globalsession：应用在Web项目中，必须在porlet环境下使用。但是如果没有这种环境，相当于session。\n\n## Spring的Bean管理配置\n\n### Spring的Bean的实例化方式\n\n#### 无参构造方式（默认）\n\n```java\n/**\n * //TODO\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class Dog {\n\n    private String name;\n    private Integer length;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getLength() {\n        return length;\n    }\n\n    public void setLength(Integer length) {\n        this.length = length;\n    }\n\n    @Override\n    public String toString() {\n        return \"Dog{\" +\n                \"name='\" + name + '\\'' +\n                \", length=\" + length +\n                '}';\n    }\n}\n\n```\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!-- Spring Bean的实例化方式--\u003e\n\n    \u003c!-- 无参构造的方式 --\u003e\n    \u003cbean id=\"dog\" class=\"learningspring.ioc.examples.demo3.Dog\"/\u003e\n\n\u003c/beans\u003e\n```\n\n\n\n#### 静态工厂实例化方式\n\n```java\n/**\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class Car {\n    private String name;\n    private Double price;\n\n    public Car() {\n    }\n\n    public Car(String name, Double price) {\n        this.name = name;\n        this.price = price;\n    }\n\n    @Override\n    public String toString() {\n        return \"Car{\" +\n                \"name='\" + name + '\\'' +\n                \", price=\" + price +\n                '}';\n    }\n}\n```\n\n```java\npackage learningspring.ioc.examples.demo3;\n\n/**\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class CarFactory {\n\n    public static Car createCar(){\n        return new Car();\n    }\n}\n```\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!-- Spring Bean的实例化方式--\u003e\n    \u003c!-- 静态工厂的方式 --\u003e\n    \u003cbean id=\"car\" class=\"learningspring.ioc.examples.demo3.CarFactory\" factory-method=\"createCar\"/\u003e\n\n\u003c/beans\u003e\n```\n\n\n\n#### 实例工厂实例化方式\n\n```java\n/**\n * //TODO\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class Dog {\n\n    private String name;\n    private Integer length;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getLength() {\n        return length;\n    }\n\n    public void setLength(Integer length) {\n        this.length = length;\n    }\n\n    @Override\n    public String toString() {\n        return \"Dog{\" +\n                \"name='\" + name + '\\'' +\n                \", length=\" + length +\n                '}';\n    }\n}\n\n```\n\n```java\n/**\n * //TODO\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class DogFactory {\n\n    public Dog createDog(){\n        return new Dog();\n    }\n}\n```\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!-- Spring Bean的实例化方式--\u003e\n\n    \u003c!-- 实例工厂的方式 --\u003e\n    \u003cbean id=\"dogFactory\" class=\"learningspring.ioc.examples.demo3.DogFactory\"/\u003e\n    \u003cbean id=\"dog2\" factory-bean=\"dogFactory\" factory-method=\"createDog\"/\u003e\n\u003c/beans\u003e\n```\n\n\n\n### Spring的属性注入方式\n\n#### 构造方法方式的属性注入\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n    \u003c!--构造方法方式的属性注入--\u003e\n    \u003cbean id=\"car\" class=\"learningspring.ioc.examples.demo3.Car\"\u003e\n        \u003cconstructor-arg name=\"name\" value=\"BWM\"/\u003e\n        \u003cconstructor-arg name=\"price\" value=\"800000\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n#### Set方法方式的属性注入\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!--Set方法方式的属性注入--\u003e\n    \u003cbean id=\"dog\" class=\"learningspring.ioc.examples.demo3.Dog\"\u003e\n        \u003cproperty name=\"name\" value=\"Golden\"/\u003e\n        \u003cproperty name=\"length\" value=\"100\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n#### 为Bean注入引用类型的数据\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n    \u003c!--构造方法方式的属性注入--\u003e\n    \u003cbean id=\"car\" class=\"learningspring.ioc.examples.demo3.Car\"\u003e\n        \u003cconstructor-arg name=\"name\" value=\"BWM\"/\u003e\n        \u003cconstructor-arg name=\"price\" value=\"800000\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!--Set方法方式的属性注入--\u003e\n    \u003cbean id=\"dog\" class=\"learningspring.ioc.examples.demo3.Dog\"\u003e\n        \u003cproperty name=\"name\" value=\"Golden\"/\u003e\n        \u003cproperty name=\"length\" value=\"100\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!--为Bean注入对象属性--\u003e\n    \u003c!--构造方法方式一样可行--\u003e\n    \u003cbean id=\"employee\" class=\"learningspring.ioc.examples.demo3.Employee\"\u003e\n        \u003cproperty name=\"name\" value=\"Chen\"/\u003e\n        \u003cproperty name=\"car\" ref=\"car\"/\u003e\n        \u003cproperty name=\"dog\" ref=\"dog\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n#### P名称空间的属性注入（Spring2.5）\n\n+ 通过引入p名称空间完成属性注入\n  + 普通属性：p:属性名=“值”\n  + 对象属性：p:属性名-ref=“值”\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:p=\"http://www.springframework.org/schema/p\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!--P名称空间的属性注入--\u003e\n    \u003cbean id=\"cat\" class=\"learningspring.ioc.examples.demo3.Cat\" p:name=\"Orange\" p:length=\"100\"/\u003e\n    \n    \u003c!--为Bean注入对象属性--\u003e\n    \u003cbean id=\"employee\" class=\"learningspring.ioc.examples.demo3.Employee\" p:cat-ref=\"cat\"\u003e\n        \u003cproperty name=\"name\" value=\"Chen\"/\u003e\n        \u003cproperty name=\"car\" ref=\"car\"/\u003e\n        \u003cproperty name=\"dog\" ref=\"dog\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n#### SpEL方式的属性注入（Spring3）\n\nSpEL：Spring Expresssion Language  的表达式语言\n\n语法：#{表达式}\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:p=\"http://www.springframework.org/schema/p\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!--SpEL表达式注入--\u003e\n    \u003cbean id=\"cat2\" class=\"learningspring.ioc.examples.demo4.Cat\"\u003e\n        \u003c!--字符串要加单引号--\u003e\n        \u003c!--也可以通过#{beanName.属性名或方法名}来通过其他bean的属性或者方法来注入--\u003e\n        \u003cproperty name=\"name\" value=\"#{'Orange'}\"/\u003e\n        \u003cproperty name=\"length\" value=\"#{101}\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n\n\n### 注入集合类型的数据\n\n```java\n/**\n * 注入集合类型的数据测试\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class CollectionBean {\n\n    private String[] strs;\n    private List\u003cString\u003e list;\n    private Set\u003cString\u003e set;\n    private Map\u003cString, String\u003e map;\n\n    public void setStrs(String[] strs) {\n        this.strs = strs;\n    }\n\n    public void setList(List\u003cString\u003e list) {\n        this.list = list;\n    }\n\n    public void setSet(Set\u003cString\u003e set) {\n        this.set = set;\n    }\n\n    public void setMap(Map\u003cString, String\u003e map) {\n        this.map = map;\n    }\n\n    @Override\n    public String toString() {\n        return \"CollectionBean{\" +\n                \"strs=\" + Arrays.toString(strs) +\n                \", list=\" + list +\n                \", set=\" + set +\n                \", map=\" + map +\n                '}';\n    }\n}\n\n```\n\n\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!--Spring的集合属性的注入--\u003e\n    \u003c!--注入数组类型--\u003e\n    \u003cbean id=\"collectionBean\" class=\"learningspring.ioc.examples.demo4.CollectionBean\"\u003e\n        \u003c!-- 注入数组类型 --\u003e\n        \u003cproperty name=\"strs\"\u003e\n            \u003clist\u003e\n                \u003cvalue\u003eTom\u003c/value\u003e\n                \u003cvalue\u003eJack\u003c/value\u003e\n            \u003c/list\u003e\n        \u003c/property\u003e\n\n        \u003c!-- 注入List集合 --\u003e\n        \u003cproperty name=\"list\"\u003e\n            \u003clist\u003e\n                \u003cvalue\u003eLucy\u003c/value\u003e\n                \u003cvalue\u003eLily\u003c/value\u003e\n            \u003c/list\u003e\n        \u003c/property\u003e\n\n        \u003c!-- 注入Set集合 --\u003e\n        \u003cproperty name=\"set\"\u003e\n            \u003cset\u003e\n                \u003cvalue\u003eaaa\u003c/value\u003e\n                \u003cvalue\u003ebbb\u003c/value\u003e\n                \u003cvalue\u003eccc\u003c/value\u003e\n            \u003c/set\u003e\n        \u003c/property\u003e\n\n        \u003c!-- 注入Map集合 --\u003e\n        \u003cproperty name=\"map\"\u003e\n            \u003cmap\u003e\n                \u003centry key=\"a\" value=\"1\"/\u003e\n                \u003centry key=\"b\" value=\"2\"/\u003e\n            \u003c/map\u003e\n        \u003c/property\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n### Spring分模块开发的配置\n\n+ 加载配置文件时，直接加载多个配置文件\n\n```java\nApplicationContext ctx = new ClassPathXmlApplicationContext(\"applicationContext1.xml\", \"applicationContext2.xml\");\n```\n\n+ 在一个配置文件中引入多个配置文件\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!--引入配置文件--\u003e\n\t\u003cimport resource=\"applicationContext2.xml\"/\u003e\n    \n\u003c/beans\u003e\n```\n\n# Spring开发中的常用注解\n\n## @Component\n\n该注解在类上使用，使用该注解就相当于在配置文件中配置了一个Bean，例如：\n\n```java\n@Component(\"userDao\")\npublic class UserDaoImpl implements UserDao {\n    @Override\n    public void save() {\n        System.out.println(\"UserDaoImpl.save\");\n    }\n}\n```\n\n相当于以下内容：\n\n```xml\n\u003cbean id=\"userDao\" class=\"learningspring.ioc.examplesannotation.demo1.UserDaoImpl\"\u003e\u003c/bean\u003e\n```\n\n该注解有3个衍生注解：\n\n+ **@Controller：修饰Web 层类**\n+ **@Service：修饰Service层类**\n+ **@Repository：修饰Dao层类**\n\n## @Value\n\n该注解用于给属性注入值，有2种用法\n\n+ 如果有set方法，则需要将该注解添加到set方法上\n+ 如果没有set方法，则需要将该注解添加到属性上\n\n```java\n/**\n * Value 注解用于属性注入\n * 当类有提供set方法时添加在set方法上\n * 如果没有，则添加到属性上\n *\n * @author Chen Rui\n * @version 1.0\n **/\n\n@Component(\"dog\")\npublic class Dog {\n    private String name;\n\n    @Value(\"100\") // 注入属性值\n    private Double length;\n\n    public Dog() {\n    }\n\n    public Dog(String name, Double length) {\n        this.name = name;\n        this.length = length;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    @Value(\"Golden\") // 注入属性值\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String toString() {\n        return \"Dog{\" +\n                \"name='\" + name + '\\'' +\n                \", length=\" + length +\n                '}';\n    }\n}\n\n```\n\n## @Autowired\n\n`@Value` 通常用于普通属性的注入。\n\n`@Autowired` 通常用于为对象类型的属性注入值，但是按照**类型**完成属性注入\n\n习惯是按照**名称**完成属性注入，所以必须让`@Autowired`注解和`@Qualifier`注解**一同使用**。\n\n（如果没有`@Qualifier`注解，修改以下例子中`@Repository`注解的值，也能编译成功）\n\n```java\n@Service(\"userService\")\npublic class UserServiceImpl implements UserService {\n\n    @Autowired\n    @Qualifier(\"userDao\")\n    private UserDao userDao;\n\n    @Override\n    public void save() {\n        System.out.println(\"UserServiceImpl.save\");\n        userDao.save();\n    }\n}\n```\n\n```java\n@Repository(\"userDao\")\npublic class UserDaoImpl implements UserDao {\n    @Override\n    public void save() {\n        System.out.println(\"UserDaoImpl.save\");\n    }\n}\n```\n\n## @Resource\n\n该注解也可以用于属性注入，通常情况下使用**@Resource注解**，默认按照**名称**完成属性注入。\n\n该注解由J2EE提供，需要导入包`javax.annotation.Resource`。\n\n`@Resource`有两个重要的属性：`name`和`type`，而Spring将`@Resource`注解的`name`属性解析为bean的名字，而`type`属性则解析为bean的类型。所以，如果使用`name`属性，则使用byName的自动注入策略，而使用`type`属性时则使用byType自动注入策略。如果既不制定`name`也不制定`type`属性，这时将通过反射机制使用byName自动注入策略。\n\n```java\n/**\n * UserController\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@Controller(\"userController\")\npublic class UserController {\n    \n    @Resource(name = \"userService\")\n    private UserService userService; \n    \n}\n```\n\n```java\n/**\n * UserService实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\n\n@Service(\"userService\")\npublic class UserServiceImpl implements UserService {\n\n\t@Resource(name = \"userDao\")\n    private UserDao userDao;\n\n    @Override\n    public void save() {\n        System.out.println(\"UserServiceImpl.save\");\n        userDao.save();\n    }\n}\n\n```\n\n```java\n/**\n * UserDao实现类\n * @author Chen Rui\n * @version 1.0\n **/\n\n@Component(\"userDao\")\npublic class UserDaoImpl implements UserDao {\n    @Override\n    public void save() {\n        System.out.println(\"UserDaoImpl.save\");\n    }\n}\n\n```\n\n## @PostConstruct 和 @PreDestroy\n\n`@PostConstruct`和`@PreDestroy`注解，前者为Bean生命周期相关的注解，在配置文件中，使用到了i`nit-method`参数来配置Bean初始化之前要执行的方法，该注解也是同样的作用。将该注解添加到想在初始化之前执行的目标方法上，就可以实现该功能，而后者则是Bean生命周期中`destroy-method`目标方法，使用该注解在指定方法上，即可实现在Bean销毁之前执行该方法。\n\n```java\n/**\n * UserDao实现类\n * @author Chen Rui\n * @version 1.0\n **/\n\n@Component(\"userDao\")\npublic class UserDaoImpl implements UserDao {\n    \n    @PostConstruct\n    public void init(){\n        System.out.println(\"UserDaoImpl.init\");\n    }\n    \n    @Override\n    public void save() {\n        System.out.println(\"UserDaoImpl.save\");\n    }\n    \n    @PreDestroy\n    public void destroy(){\n        System.out.println(\"UserDaoImpl.destroy\");\n    }\n}\n```\n\n## @Scope\n\nBean的作用范围的注解，默认为singleton（单例）\n\n可选值：\n\n+ singleton\n+ prototype\n+ request\n+ session\n+ globalsession\n\n```java\n/**\n * UserDao实现类\n * @author Chen Rui\n * @version 1.0\n **/\n\n@Component(\"userDao\")\n@Scope // 默认为singleton\npublic class UserDaoImpl implements UserDao {\n\n    @PostConstruct\n    public void init(){\n        System.out.println(\"UserDaoImpl.init\");\n    }\n\n    @Override\n    public void save() {\n        System.out.println(\"UserDaoImpl.save\");\n    }\n\n    @PreDestroy\n    public void destroy(){\n        System.out.println(\"UserDaoImpl.destroy\");\n    }\n}\n```\n\n## 基于XML配置和基于注解配置的对比\n\n|                | 基于XML的配置                                                | 基于注解的配置                                               |\n| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |\n| Bean的定义     | \\\u003cbean id=\"Bean的id\" class=\"类的全路径\"/\u003e                    | @Component或衍生注解@Controller，@Service和@Repository       |\n| Bean的名称     | 通过id或name指定                                             | @Component(“Bean的id”)                                       |\n| Bean的属性注入 | \\\u003cproperty\u003e或者通过p命名空间                                 | 通过注解@Autowired 按类型注入\u003cbr /\u003e通过@Qualifier按名称注入  |\n| Bean的生命周期 | init-method指定Bean初始化前执行的方法，destroy-method指定Bean销毁前执行的方法 | @PostConstruct 对应于int-method\u003cbr /\u003e@PreDestroy 对应于destroy-method |\n| Bean的作用域   | 在bean标签中配置scope属性                                    | @Scope, 默认是singleton\u003cbr /\u003e配置多例可以在目标类上使用@Scope(“prototype”) |\n| 使用场景       | Bean来自第三方，可以使用在任何场景                           | Bean的实现类由自己维护                                       |\n\nXML可以适用于任何场景，就算Bean来自第三方也可以适用XML方式来管理。而注解方式就无法在此场景下使用，注解方式可以让开发的过程更加方便，但前提是Bean由自己维护，这样才能在源码中添加注解。\n\n所以可以使用**两者混合**的方式来开发项目，使用**XML配置文件来管理Bean，使用注解来进行属性注入**\n\n# Spring AOP\n\n## AOP的概述\n\n即**面向切面编程**，通过**预编译**方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行**隔离**，从而使得业务逻辑各部分之间的**耦合度降低**，提高程序的**可重用性**，同时提高了开发的效率。\n\n## AOP的案例（应用场景）\n\n背景：某项目已经写好了保存到数据库的方法。假设现在需要添加一个新的功能，例如权限校验，在保存到数据库之前要对用户权限进行校验。\n\n```java\npublic class UserDaoImpl implements UserDao {\n    @Override\n    public void save(){\n        ...\n    }\n}\n```\n\n现在需要多加一个需求，在用户将数据保存到数据库之前，进行权限校验。\n\n此时通常就会在该方法中添加一个方法来进行权限校验然后在save方法中调用。\n\n```java\npublic class UserDaoImpl implements UserDao {\n    @Override\n    public void save(){\n        checkPri();\n        // 保存到数据库\n    }\n    \n    private void checkPri(){\n        // 权限校验\n    }\n}\n```\n\n用这样的方法来实现，弊端就是只能在这一个类中使用，通常一个项目中有许多的方法都可能需要执行权限校验，此时就要在每个类中编写同样的代码，所以该方法并不科学。\n\n此时就有了一个更好的方法，即**纵向继承**。\n\n定义一个通用的Dao，在通用的Dao中编写权限校验的方法。\n\n```java\npublic class BaseDao{\n    public void checkPri(){\n        // 权限校验\n    }\n}\n```\n\n然后每一个不同的类都去继承这个类，再调用该方法\n\n```java\npublic class UserDaoImpl extends BaseDao implements UserDao{\n    @Override\n    public void save(){\n        checkPri();\n        // 保存到数据库\n    }\n}\n```\n\n此时就只需要维护`BaseDao`中的一份代码就可以，大大减轻了工作量，提高了效率。\n\n但AOP的思想更高一步，不采用纵向继承，而采用**横向抽取**来取代\n\n```java\npublic class UserDaoImpl implements UserDao{\n    @Override\n    public void save(){\n        // 保存到数据库\n    }\n}\n```\n\n横向抽取机制实质上就是**代理机制**，通过创建`UserDaoImpl`类的代理类，通过代理类来调用权限校验的方法。\n\n## AOP底层实现原理\n\nAOP的实现使用了动态代理技术，动态代理分为两种\n\n+ JDK动态代理：只能对实现了接口的类产生代理\n+ Cglib动态代理（类似于javassist的第三方代理技术）：对没有实现接口的类产生代理对象，即生成子类对象。\n\n### JDK动态代理\n\n#### JDK动态代理案例\n\n该案例实现一个计算器的日志功能\n\n首先创建一个接口`Calculator`\n\n```java\n/**\n * 计算器接口\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic interface Calculator {\n\n    /**\n     * 加法\n     * @param a 实数\n     * @param b 实数\n     * @return 相加结果\n     */\n    int add(int a, int b);\n\n    /**\n     * 减法\n     * @param a 实数,被减数\n     * @param b 实数,减数\n     * @return 相减结果\n     */\n    int sub(int a, int b);\n\n    /**\n     * 乘法\n     * @param a 实数\n     * @param b 实数\n     * @return 相乘结果\n     */\n    int mul(int a, int b);\n\n    /**\n     * 除法\n     * @param a 实数,被除数\n     * @param b 实数,除数\n     * @return 相除结果\n     */\n    int div(int a, int b);\n}\n```\n\n接着创建一个类`CalculatorImpl`来实现该接口并重写方法\n\n```java\n/**\n * 计算器实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class CalculatorImpl implements Calculator {\n\n    @Override\n    public int add(int a, int b) {\n        return a + b;\n    }\n\n    @Override\n    public int sub(int a, int b) {\n        return a - b;\n    }\n\n    @Override\n    public int mul(int a, int b) {\n        return a * b;\n    }\n\n    @Override\n    public int div(int a, int b) {\n        if (b == 0){\n            System.out.println(\"除数不能为0\");\n            return 0;\n        }\n        return  a / b;\n    }\n}\n\n```\n\n在测试类中测试该计算器代码\n\n```java\n/**\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AppTest {\n    \n    @Test\n    public void test() {\n        Calculator target = new CalculatorImpl();\n        int a = 10;\n        int b = 10;\n        System.out.println(\"res --\u003e \" + target.add(a, b));\n\n        System.out.println(\"res --\u003e \" + target.mul(a, b));\n\n        System.out.println(\"res --\u003e \" + target.sub(a, b));\n\n        System.out.println(\"res --\u003e \" + target.div(a, b));\n    }\n}\n```\n\n此时控制台的输出结果为：\n\n```\nres --\u003e 20\nres --\u003e 100\nres --\u003e 0\nres --\u003e 1\n```\n\n现在为该计算器增加**打印日志**的功能\n\n创建一个计算器的代理类`CalculatorLoggingProxy`，在类中首先定义被代理的目标对象target，并通过构造函数进行赋值。\n\n```java\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Arrays;\nimport java.util.Date;\n\n/**\n * 计算器代理类\n * 实现扩展打印日志功能\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class CalculatorProxy {\n    /**\n     * 被代理的对象\n     */\n    private Calculator target;\n\n    public CalculatorProxy(Calculator target) {\n        this.target = target;\n    }\n\n    public Calculator createProxy(){\n        Calculator proxy;\n\n        ClassLoader classLoader = target.getClass().getClassLoader();\n\n        Class[] interfaces = new Class[]{Calculator.class};\n\n        InvocationHandler handler = new InvocationHandler() {\n            /**\n             * @param proxy     正在返回的代理对象，一般在invoke方法中都不使用该对象\n             *                  如果使用该对象，则会引发栈内存溢出。因为会循环调用invoke方法。\n             * @param method    正在被调用的方法\n             * @param args      调用方式时传入的参数\n             * @return\n             * @throws Throwable\n             */\n            @Override\n            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                // 获取方法名\n                String methodName = method.getName();\n                // 输出日志逻辑\n                System.out.println(new Date() + \": The method \" + methodName + \" begins with \" + Arrays.asList(args));\n                // 执行方法\n                Object result = method.invoke(target, args);\n                // 输出日志逻辑\n                System.out.println(new Date() + \": The method \" + methodName + \" ends with \" + result);\n                return result;\n            }\n        };\n\n        proxy = (Calculator) Proxy.newProxyInstance(classLoader,interfaces,handler);\n\n        return proxy;\n    }\n}\n\n```\n\n此时重新编写测试方法\n\n```java\n/**\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AppTest {\n    \n    @Test\n    public void test() {\n        Calculator target = new CalculatorImpl();\n        // 创建代理对象\n        Calculator proxy = new CalculatorProxy(target).createProxy();\n        int a = 10;\n        int b = 10;\n        System.out.println(\"res --\u003e \" + proxy.add(a, b));\n\n        System.out.println(\"res --\u003e \" + proxy.mul(a, b));\n\n        System.out.println(\"res --\u003e \" + proxy.sub(a, b));\n\n        System.out.println(\"res --\u003e \" + proxy.div(a, b));\n    }\n}\n```\n\n到此就完成了在不改变`CalculatorImpl`类的源代码的情况下，实现对计算器的功能增加，实现了日志打印的功能。此时控制台的打印内容为\n\n```\nSun Mar 17 20:36:26 CST 2019: The method add begins with [10, 10]\nSun Mar 17 20:36:26 CST 2019: The method add ends with 20\nres --\u003e 20\nSun Mar 17 20:36:26 CST 2019: The method mul begins with [10, 10]\nSun Mar 17 20:36:26 CST 2019: The method mul ends with 100\nres --\u003e 100\nSun Mar 17 20:36:26 CST 2019: The method sub begins with [10, 10]\nSun Mar 17 20:36:26 CST 2019: The method sub ends with 0\nres --\u003e 0\nSun Mar 17 20:36:26 CST 2019: The method div begins with [10, 10]\nSun Mar 17 20:36:26 CST 2019: The method div ends with 1\nres --\u003e 1\n```\n\n\n\n### Cglib动态代理\n\n#### Cglib动态代理案例\n\n同样来实现一个对计算器来增加打印日志功能\n\n首先创建计算器类`Calculator`\n\n```java\n/**\n * 计算器类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class Calculator {\n\n    public int add(int a, int b) {\n        return a + b;\n    }\n\n    public int sub(int a, int b) {\n        return a - b;\n    }\n\n    public int mul(int a, int b) {\n        return a * b;\n    }\n\n    public int div(int a, int b) {\n        if (b == 0){\n            System.out.println(\"除数不能为0\");\n            return 0;\n        }\n        return  a / b;\n    }\n}\n\n```\n\n此时需要导入cglib的jar包，在maven中添加依赖\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecglib\u003c/groupId\u003e\n    \u003cartifactId\u003ecglib\u003c/artifactId\u003e\n    \u003cversion\u003e2.2.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n接着创建计算器的代理类`CalculatorProxy`并且实现`MethodInterceptor`接口并重写`intercept`方法。\n\n在类中首先定义被代理的目标对象，并通过构造函数进行赋值。然后创建`createProxy()`方法返回被增强的计算器对象。\n\n```java\n/**\n * 计算器代理类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class CalculatorProxy implements MethodInterceptor {\n\n    /**\n     * 被代理的对象\n     */\n    private Calculator target;\n\n    public CalculatorProxy(Calculator target) {\n        this.target = target;\n    }\n\n    public Calculator createProxy(){\n\n        // 1.创建cglib的核心类对象\n        Enhancer enhancer = new Enhancer();\n\n        // 2.设置父类\n        enhancer.setSuperclass(target.getClass());\n\n        // 3.设置回调（类似于jdk动态代理中的InvocationHandler对象）\n        enhancer.setCallback(this);\n\n        // 4.创建代理对象\n        Calculator proxy = (Calculator) enhancer.create();\n\n        return proxy;\n    }\n\n    @Override\n    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {\n        // 获取方法名\n        String methodName = method.getName();\n        // 输出日志逻辑\n        System.out.println(new Date() + \": The method \" + methodName + \" begins with \" + Arrays.asList(args));\n        // 执行方法\n        Object result = methodProxy.invokeSuper(proxy, args);\n        // 输出日志逻辑\n        System.out.println(new Date() + \": The method \" + methodName + \" ends with \" + result);\n        return result;\n    }\n}\n\n```\n\n## Spring中的AOP实现——AspectJ\n\n### AOP开发中的相关术语\n\n```java\npublic class UserDao{\n    public void save(){}\n    \n    public void query(){}\n    \n    public void update(){}\n    \n    public void delete(){}\n}\n```\n\n- joinpoint(连接点) ： 可以被拦截到的点。save(), query(),update(),delete()方法都可以增强，这些方法就可以称为连接点。\n- pointcut(切入点)：真正被拦截到的点。在实际开发中，可以只对save()方法进行增强，那么save()方法就是切入点。\n- advice(增强)：方法层面的增强，现在可以对save()方法进行权限校验，权限校验(checkPri())的方法称为增强。\n- introduction(引介)：类层面的增强。\n- target(目标)：被增强的对象。\n- weaving(织入)：将增强(advice)应用到目标(target)的过程\n- proxy(代理)：代理对象，被增强以后的代理对象\n- aspect(切面)：多个增强(advice)和多个切入点(pointcut)的组合\n\n### AspectJ的XML配置案例\n\n首先创建一个接口`ProductDao`，在里面定义添加商品，查询商品，修改商品，删除商品方法。\n\n```java\n/**\n * ProductDao\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic interface ProductDao {\n\n    /**\n     * 添加商品\n     */\n    void save();\n\n    /**\n     * 删除商品\n     */\n    void delete();\n\n    /**\n     * 修改商品\n     */\n    void modify();\n\n    /**\n     * 查询商品\n     */\n    void query();\n}\n\n```\n\n接着创建一个类`ProductDaoImpl`来实现该接口\n\n```java\n/**\n * ProductDao的实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductDaoImpl implements ProductDao {\n\n    @Override\n    public void save() {\n        System.out.println(\"添加商品\");\n    }\n\n    @Override\n    public void delete() {\n        System.out.println(\"删除商品\");\n    }\n    \n    @Override\n    public void modify() {\n        System.out.println(\"修改商品\");\n    }\n    \n    @Override\n    public void query() {\n        System.out.println(\"查询商品\");\n    }\n    \n}\n\n```\n\n现在目的就是给`save()`方法进行增强，使得在调用`save()`方法前进行权限校验。\n\n要实现此功能，先创建一个**增强类**，或者叫**切面类**。里面编写要增强的功能，例如权限校验。\n\n创建增强类`ProductEnhancer`\n\n```java\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductEnhancer {\n\n    public void checkPri(){\n        System.out.println(\"【前置增强】权限校验\");\n    }\n\n}\n\n```\n\n然后创建配置文件`aspectj-xml.xml`来配置，该文件名此案例仅用于演示，实际开发中不要采取此名，依据实际需求编写。\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans.xsd\n    http://www.springframework.org/schema/aop\n    http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 配置目标对象，即被增强的对象 --\u003e\n    \u003cbean id=\"productDao\" class=\"learningspring.aop.aspectj.xml.demo2.ProductDaoImpl\"/\u003e\n\n    \u003c!-- 将增强类(切面类)交给Spring管理 --\u003e\n    \u003cbean id=\"productEnhancer\" class=\"learningspring.aop.aspectj.xml.demo2.ProductEnhancer\"/\u003e\n    \n    \u003c!-- 通过对AOP的配置完成对目标对象产生代理 --\u003e\n    \u003caop:config\u003e\n        \u003c!-- 表达式配置哪些类的哪些方法需要进行增强 --\u003e\n        \u003c!-- 对ProductDaoImpl类中的save方法进行增强 --\u003e\n        \u003c!--\n        “*” 表示任意返回值类型\n        “..” 表示任意参数\n        --\u003e\n        \u003caop:pointcut id=\"pointcut1\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.save(..))\"/\u003e\n\n        \u003c!-- 配置切面 --\u003e\n        \u003caop:aspect ref=\"productEnhancer\"\u003e\n            \u003c!-- 前置增强 --\u003e\n            \u003c!-- 实现在调用save方法之前调用checkPri方法来进行权限校验--\u003e\n            \u003caop:before method=\"checkPri\" pointcut-ref=\"pointcut1\"/\u003e\n        \u003c/aop:aspect\u003e\n    \u003c/aop:config\u003e\n    \n\u003c/beans\u003e\n```\n\n至此切入点及切面都已配置完成，编写测试类和方法\n\n```java\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\n\nimport javax.annotation.Resource;\n\n/**\n * AspectJ的XML方式配置测试类\n *\n * @author Chen Rui\n * @version 1.0\n **/\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(\"classpath:aspectj-xml.xml\")\npublic class AppTest {\n\n    @Resource(name = \"productDao\")\n    private ProductDao productDao;\n\n    @Test\n    public void test(){\n        // 对save方法进行增强\n        productDao.save();\n\n        productDao.delete();\n        \n        productDao.modify();\n        \n        productDao.query();\n    }\n}\n\n```\n\n运行`test()`方法，控制台打印结果如下：\n\n```\n【前置增强】权限校验\n添加商品\n删除商品\n修改商品\n查询商品\n```\n\n至此就实现了在不修改`ProductDaoImpl`类的情况下，对其中的`save()`方法进行增强。\n\n### Spring中常用的增强类型\n\n#### 前置增强\n\n在目标方法执行之前执行，可以获得切入点的信息\n\n修改之前的`ProductEnhancer`类的`checkPri()`方法的参数。\n\n```java\nimport org.aspectj.lang.JoinPoint;\n\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductEnhancer {\n\n    public void checkPri(JoinPoint joinPoint){\n        System.out.println(\"【前置增强】权限校验\" + joinPoint);\n    }\n\n}\n```\n\n执行测试方法，控制台输出\n\n```\n【前置增强】权限校验execution(void learningspring.aop.aspectj.xml.demo2.ProductDao.save())\n添加商品\n删除商品\n修改商品\n查询商品\n```\n\n\n\n#### 后置增强\n\n在目标方法执行之后执行，可以获得方法的返回值\n\n首先修改`ProductDao`中的`delete()`方法的返回值类型，改成String\n\n```java\n/**\n * ProductDao\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic interface ProductDao {\n\n    /**\n     * 添加商品\n     */\n    void save();\n\n    /**\n     * 删除商品\n     */\n    String delete();\n\n    /**\n     * 修改商品\n     */\n    void modify();\n\n    /**\n     * 查询商品\n     */\n    void query();\n}\n\n```\n\n再修改`ProductDaoImpl`中的`delete()`方法\n\n```java\n/**\n * ProductDao的实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductDaoImpl implements ProductDao {\n\n    @Override\n    public void save() {\n        System.out.println(\"添加商品\");\n    }\n\n    @Override\n    public String delete() {\n        System.out.println(\"删除商品\");\n        return new Date().toString();\n    }\n\n    @Override\n    public void modify() {\n        System.out.println(\"修改商品\");\n    }\n\n    @Override\n    public void query() {\n        System.out.println(\"查询商品\");\n    }\n}\n\n```\n\n修改`ProductEnhancer`类，添加`writeLog()`方法，实现写日志功能\n\n```java\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductEnhancer {\n\n    /**\n     * 前置增强案例\n     * 在调用save方法之前进行权限校验\n     * @param joinPoint 切入点对象\n     */\n    public void checkPri(JoinPoint joinPoint){\n        System.out.println(\"【前置增强】权限校验\" + joinPoint);\n    }\n\n    /**\n     * 后置增强案例\n     * 在调用delete方法之后，写入日志记录操作时间\n     * @param result 目标方法返回的对象\n     */\n    public void writeLog(Object result){\n        System.out.println(\"【后置增强】写入日志 操作时间：\" + result.toString());\n    }\n}\n```\n\n然后修改`aspectj.xml`配置文件，配置新的**切入点**和**切面**\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans.xsd\n    http://www.springframework.org/schema/aop\n    http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 配置目标对象，即被增强的对象 --\u003e\n    \u003cbean id=\"productDao\" class=\"learningspring.aop.aspectj.xml.demo2.ProductDaoImpl\"/\u003e\n\n    \u003c!-- 将增强类(切面类)交给Spring管理 --\u003e\n    \u003cbean id=\"productEnhancer\" class=\"learningspring.aop.aspectj.xml.demo2.ProductEnhancer\"/\u003e\n    \n    \u003c!-- 通过对AOP的配置完成对目标对象产生代理 --\u003e\n    \u003caop:config\u003e\n        \u003c!-- 表达式配置哪些类的哪些方法需要进行增强 --\u003e\n        \u003c!-- 对ProductDaoImpl类中的save方法进行增强 --\u003e\n        \u003c!--\n        “*” 表示任意返回值类型\n        “..” 表示任意参数\n        --\u003e\n        \u003c!-- 前置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut1\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.save(..))\"/\u003e\n        \n        \u003c!-- 后置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut2\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.delete(..))\"/\u003e\n\n        \u003c!-- 配置切面 --\u003e\n        \u003caop:aspect ref=\"productEnhancer\"\u003e\n            \u003c!-- 前置增强 --\u003e\n            \u003c!-- 实现在调用save方法之前调用checkPri方法来进行权限校验--\u003e\n            \u003caop:before method=\"checkPri\" pointcut-ref=\"pointcut1\"/\u003e\n            \n            \u003c!-- 后置增强 --\u003e\n            \u003c!-- returning里面的值必须和writeLog()方法里的参数名相同，本案例为result--\u003e\n            \u003caop:after-returning method=\"writeLog\" returning=\"result\" pointcut-ref=\"pointcut2\"/\u003e\n        \u003c/aop:aspect\u003e\n    \u003c/aop:config\u003e\n\n\u003c/beans\u003e\n```\n\n执行测试方法，控制台打印结果\n\n```\n【前置增强】权限校验execution(void learningspring.aop.aspectj.xml.demo2.ProductDao.save())\n添加商品\n删除商品\n【后置增强】写入日志 操作时间：Tue Mar 19 15:59:48 CST 2019\n修改商品\n查询商品\n```\n\n#### 环绕增强\n\n在目标方法执行之前和之后都执行\n\n利用环绕增强来实现在调用`modify()`方法前后进行性能监控\n\n首先修改`ProductEnhancer`类，添加`monitor()`方法\n\n```java\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductEnhancer {\n\n    /**\n     * 前置增强案例\n     * 在调用save方法之前进行权限校验\n     * @param joinPoint 切入点对象\n     */\n    public void checkPri(JoinPoint joinPoint){\n        System.out.println(\"【前置增强】权限校验\" + joinPoint);\n    }\n\n    /**\n     * 后置增强案例\n     * 在调用delete方法之后，写入日志记录操作时间\n     * @param result 目标方法返回的对象\n     */\n    public void writeLog(Object result){\n        System.out.println(\"【后置增强】写入日志 操作时间：\" + result.toString());\n    }\n\n    /**\n     * 环绕增强\n     * 在调用modify方法前后，显示性能参数\n     * @param joinPoint 切入点对象\n     * @throws Throwable 可抛出的异常\n     */\n    public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable{\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        Object obj = joinPoint.proceed();\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        return obj;\n    }\n}\n\n```\n\n然后再修改`aspectj.xml`配置文件，添加新的**切入点**和**切面**\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans.xsd\n    http://www.springframework.org/schema/aop\n    http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 配置目标对象，即被增强的对象 --\u003e\n    \u003cbean id=\"productDao\" class=\"learningspring.aop.aspectj.xml.demo2.ProductDaoImpl\"/\u003e\n\n    \u003c!-- 将增强类(切面类)交给Spring管理 --\u003e\n    \u003cbean id=\"productEnhancer\" class=\"learningspring.aop.aspectj.xml.demo2.ProductEnhancer\"/\u003e\n    \n    \u003c!-- 通过对AOP的配置完成对目标对象产生代理 --\u003e\n    \u003caop:config\u003e\n        \u003c!-- 表达式配置哪些类的哪些方法需要进行增强 --\u003e\n        \u003c!-- 对ProductDaoImpl类中的save方法进行增强 --\u003e\n        \u003c!--\n        “*” 表示任意返回值类型\n        “..” 表示任意参数\n        --\u003e\n        \u003c!-- 前置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut1\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.save(..))\"/\u003e\n\n        \u003c!-- 后置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut2\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.delete(..))\"/\u003e\n\n        \u003c!-- 环绕增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut3\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.modify(..))\"/\u003e\n\n        \u003c!-- 配置切面 --\u003e\n        \u003caop:aspect ref=\"productEnhancer\"\u003e\n            \u003c!-- 前置增强 --\u003e\n            \u003c!-- 实现在调用save方法之前调用checkPri方法来进行权限校验--\u003e\n            \u003caop:before method=\"checkPri\" pointcut-ref=\"pointcut1\"/\u003e\n\n            \u003c!-- 后置增强 --\u003e\n            \u003c!-- returning里面的值必须和writeLog()方法里的参数名相同，本案例为result--\u003e\n            \u003caop:after-returning method=\"writeLog\" returning=\"result\" pointcut-ref=\"pointcut2\"/\u003e\n\n            \u003c!-- 环绕增强 --\u003e\n            \u003caop:around method=\"monitor\" pointcut-ref=\"pointcut3\"/\u003e\n\n        \u003c/aop:aspect\u003e\n    \u003c/aop:config\u003e\n\n\u003c/beans\u003e\n```\n\n运行测试方法，控制台打印结果：\n\n```\n【前置增强】权限校验execution(void learningspring.aop.aspectj.xml.demo2.ProductDao.save())\n添加商品\n删除商品\n【后置增强】写入日志 操作时间：Tue Mar 19 15:58:49 CST 2019\n【环绕增强】当前空闲内存185MB\n修改商品\n【环绕增强】当前空闲内存185MB\n查询商品\n```\n\n#### 异常抛出增强\n\n在程序出现异常时执行\n\n利用异常抛出增强来实现获取异常信息的功能\n\n首先修改`ProductDaoImpl`中的`query()`方法，使该方法抛出异常\n\n```java\n/**\n * ProductDao的实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductDaoImpl implements ProductDao {\n\n    @Override\n    public void save() {\n        System.out.println(\"添加商品\");\n    }\n\n    @Override\n    public void query() {\n        System.out.println(\"查询商品\");\n        int a = 1/0;\n    }\n\n    @Override\n    public void modify() {\n        System.out.println(\"修改商品\");\n    }\n\n    @Override\n    public String delete() {\n        System.out.println(\"删除商品\");\n        return new Date().toString();\n    }\n}\n```\n\n接着修改`ProductEnhancer`类，添加`exception()`方法\n\n```java\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductEnhancer {\n\n    /**\n     * 前置增强案例\n     * 在调用save方法之前进行权限校验\n     * @param joinPoint 切入点对象\n     */\n    public void checkPri(JoinPoint joinPoint){\n        System.out.println(\"【前置增强】权限校验\" + joinPoint);\n    }\n\n    /**\n     * 后置增强案例\n     * 在调用delete方法之后，写入日志记录操作时间\n     * @param result 目标方法返回的对象\n     */\n    public void writeLog(Object result){\n        System.out.println(\"【后置增强】写入日志 操作时间：\" + result.toString());\n    }\n\n    /**\n     * 环绕增强\n     * 在调用modify方法前后，显示性能参数\n     * @param joinPoint 切入点对象\n     * @throws Throwable 可抛出的异常\n     */\n    public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable{\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        Object obj = joinPoint.proceed();\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        return obj;\n    }\n\n    /**\n     * 异常抛出增强\n     * 在调用query时若抛出异常则打印异常信息\n     * @param ex 异常对象\n     */\n    public void exception(Throwable ex){\n        System.out.println(\"【异常抛出增强】\" + \"异常信息：\" +ex.getMessage());\n    }\n}\n\n```\n\n然后再修改`aspectj-xml.xml`配置文件，添加新的**切入点**和**切面**\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans.xsd\n    http://www.springframework.org/schema/aop\n    http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 配置目标对象，即被增强的对象 --\u003e\n    \u003cbean id=\"productDao\" class=\"learningspring.aop.aspectj.xml.demo2.ProductDaoImpl\"/\u003e\n\n    \u003c!-- 将增强类(切面类)交给Spring管理 --\u003e\n    \u003cbean id=\"productEnhancer\" class=\"learningspring.aop.aspectj.xml.demo2.ProductEnhancer\"/\u003e\n    \n    \u003c!-- 通过对AOP的配置完成对目标对象产生代理 --\u003e\n    \u003caop:config\u003e\n        \u003c!-- 表达式配置哪些类的哪些方法需要进行增强 --\u003e\n        \u003c!-- 对ProductDaoImpl类中的save方法进行增强 --\u003e\n        \u003c!--\n        “*” 表示任意返回值类型\n        “..” 表示任意参数\n        --\u003e\n        \u003c!-- 前置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut1\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.save(..))\"/\u003e\n\n        \u003c!-- 后置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut2\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.delete(..))\"/\u003e\n\n        \u003c!-- 环绕增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut3\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.modify(..))\"/\u003e\n\n        \u003c!-- 异常抛出增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut4\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.query(..))\"/\u003e\n\n        \u003c!-- 配置切面 --\u003e\n        \u003caop:aspect ref=\"productEnhancer\"\u003e\n            \u003c!-- 前置增强 --\u003e\n            \u003c!-- 实现在调用save方法之前调用checkPri方法来进行权限校验--\u003e\n            \u003caop:before method=\"checkPri\" pointcut-ref=\"pointcut1\"/\u003e\n\n            \u003c!-- 后置增强 --\u003e\n            \u003c!-- returning里面的值必须和writeLog()方法里的参数名相同，本案例为result--\u003e\n            \u003caop:after-returning method=\"writeLog\" returning=\"result\" pointcut-ref=\"pointcut2\"/\u003e\n\n            \u003c!-- 环绕增强 --\u003e\n            \u003caop:around method=\"monitor\" pointcut-ref=\"pointcut3\"/\u003e\n\n            \u003c!-- 异常抛出增强 --\u003e\n            \u003caop:after-throwing method=\"exception\" throwing=\"ex\" pointcut-ref=\"pointcut4\"/\u003e\n        \u003c/aop:aspect\u003e\n    \u003c/aop:config\u003e\n\n\u003c/beans\u003e\n```\n\n最后执行测试方法，控制台输出结果：\n\n```\n【前置增强】权限校验execution(void learningspring.aop.aspectj.xml.demo2.ProductDao.save())\n添加商品\n删除商品\n【后置增强】写入日志 操作时间：Tue Mar 19 15:58:16 CST 2019\n【环绕增强】当前空闲内存183MB\n修改商品\n【环绕增强】当前空闲内存183MB\n查询商品\n【异常抛出增强】异常信息：/ by zero\n```\n\n#### 最终增强\n\n无论代码是否有异常最终都会执行\n\n继续在异常抛出增强的代码修改，实现无论是否抛出异常都会打印当前时间信息\n\n首先修改`ProductEnhancer`类，添加`finallyAdvice()`方法\n\n```java\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductEnhancer {\n\n    /**\n     * 前置增强案例\n     * 在调用save方法之前进行权限校验\n     * @param joinPoint 切入点对象\n     */\n    public void checkPri(JoinPoint joinPoint){\n        System.out.println(\"【前置增强】权限校验\" + joinPoint);\n    }\n\n    /**\n     * 后置增强案例\n     * 在调用delete方法之后，写入日志记录操作时间\n     * @param result 目标方法返回的对象\n     */\n    public void writeLog(Object result){\n        System.out.println(\"【后置增强】写入日志 操作时间：\" + result.toString());\n    }\n\n    /**\n     * 环绕增强\n     * 在调用modify方法前后，显示性能参数\n     * @param joinPoint 切入点对象\n     * @throws Throwable 可抛出的异常\n     */\n    public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable{\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        Object obj = joinPoint.proceed();\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        return obj;\n    }\n\n    /**\n     * 异常抛出增强\n     * 在调用query时若抛出异常则打印异常信息\n     * @param ex 异常对象\n     */\n    public void exception(Throwable ex){\n        System.out.println(\"【异常抛出增强】\" + \"异常信息：\" +ex.getMessage());\n    }\n\n    /**\n     * 最终增强\n     * 无论query方法是否抛出异常都打印当前时间\n     */\n    public void finallyAdvice(){\n        System.out.println(\"【最终增强】\" + new Date().toString());\n    }\n}\n\n```\n\n修改`aspectj.xml`配置文件，添加新的**切面**\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans.xsd\n    http://www.springframework.org/schema/aop\n    http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 配置目标对象，即被增强的对象 --\u003e\n    \u003cbean id=\"productDao\" class=\"learningspring.aop.aspectj.xml.demo2.ProductDaoImpl\"/\u003e\n\n    \u003c!-- 将增强类(切面类)交给Spring管理 --\u003e\n    \u003cbean id=\"productEnhancer\" class=\"learningspring.aop.aspectj.xml.demo2.ProductEnhancer\"/\u003e\n    \n    \u003c!-- 通过对AOP的配置完成对目标对象产生代理 --\u003e\n    \u003caop:config\u003e\n        \u003c!-- 表达式配置哪些类的哪些方法需要进行增强 --\u003e\n        \u003c!-- 对ProductDaoImpl类中的save方法进行增强 --\u003e\n        \u003c!--\n        “*” 表示任意返回值类型\n        “..” 表示任意参数\n        --\u003e\n        \u003c!-- 前置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut1\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.save(..))\"/\u003e\n\n        \u003c!-- 后置增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut2\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.delete(..))\"/\u003e\n\n        \u003c!-- 环绕增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut3\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.modify(..))\"/\u003e\n\n        \u003c!-- 异常抛出增强的切入点配置 --\u003e\n        \u003caop:pointcut id=\"pointcut4\" expression=\"execution(* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.query(..))\"/\u003e\n\n        \u003c!-- 配置切面 --\u003e\n        \u003caop:aspect ref=\"productEnhancer\"\u003e\n            \u003c!-- 前置增强 --\u003e\n            \u003c!-- 实现在调用save方法之前调用checkPri方法来进行权限校验--\u003e\n            \u003caop:before method=\"checkPri\" pointcut-ref=\"pointcut1\"/\u003e\n\n            \u003c!-- 后置增强 --\u003e\n            \u003c!-- returning里面的值必须和writeLog()方法里的参数名相同，本案例为result--\u003e\n            \u003caop:after-returning method=\"writeLog\" returning=\"result\" pointcut-ref=\"pointcut2\"/\u003e\n\n            \u003c!-- 环绕增强 --\u003e\n            \u003caop:around method=\"monitor\" pointcut-ref=\"pointcut3\"/\u003e\n\n            \u003c!-- 异常抛出增强 --\u003e\n            \u003caop:after-throwing method=\"exception\" throwing=\"ex\" pointcut-ref=\"pointcut4\"/\u003e\n\n            \u003c!-- 最终增强 --\u003e\n            \u003caop:after method=\"finallyAdvice\" pointcut-ref=\"pointcut4\"/\u003e\n        \u003c/aop:aspect\u003e\n    \u003c/aop:config\u003e\n\n\u003c/beans\u003e\n```\n\n最后运行测试代码，控制台输出结果：\n\n```\n【前置增强】权限校验execution(void learningspring.aop.aspectj.xml.demo2.ProductDao.save())\n添加商品\n删除商品\n【后置增强】写入日志 操作时间：Tue Mar 19 15:57:01 CST 2019\n【环绕增强】当前空闲内存183MB\n修改商品\n【环绕增强】当前空闲内存183MB\n查询商品\n【最终增强】Tue Mar 19 15:57:01 CST 2019\n【异常抛出增强】异常信息：/ by zero\n```\n\n### AOP切入点表达式语法\n\nAOP切入点表达式是基于execution的函数完成的\n\n语法：**[访问修饰符] 方法返回值 包名.类名.方法名(参数)**\n\n“*” 表示任意返回值类型\n“..” 表示任意参数\n\n+ `public void learningspring.aop.aspectj.xml.demo2.ProductDaoImpl.save(..) `：具体到某个增强的方法\n+ `* *.*.*.*Dao.save(..) `：所有包下的所有以Dao结尾的类中的save方法都会被增强\n+ `* learningspring.aop.aspectj.xml.demo2.ProductDaoImpl+.save(..) `：ProductDaoImpl及其子类的save方法都会被增强\n+ `* learningspring.aop.aspectj.xml..*.*(..)`：xml包及其子包的所有类的方法都会被增强\n\n### AspectJ的注解配置案例\n\n首先也是创建一个接口`ProductDao`\n\n```java\n/**\n * ProductDao接口\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic interface ProductDao {\n\n    /**\n     * 添加商品\n     */\n    void save();\n\n    /**\n     * 查询商品\n     */\n    void query();\n\n    /**\n     * 修改商品\n     */\n    void modify();\n\n    /**\n     * 删除商品\n     */\n    String delete();\n}\n```\n\n然后创建一个Dao实现类`ProductDaoImpl`\n\n```java\n/**\n * ProductDao的实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class ProductDaoImpl implements ProductDao {\n\n    @Override\n    public void save() {\n        System.out.println(\"添加商品\");\n    }\n\n    @Override\n    public String delete() {\n        System.out.println(\"删除商品\");\n        return new Date().toString();\n    }\n\n    @Override\n    public void modify() {\n        System.out.println(\"修改商品\");\n    }\n\n    @Override\n    public void query() {\n        System.out.println(\"查询商品\");\n        int a = 1/0;\n    }\n}\n```\n\n接着创建**增强类**`ProductEnhancer`，在该类里面使用注解\n\n使用`@Pointcut`注解可以配置切入点信息，在较多的方法都要使用同一个增强时，就可以配置一个切入点让目标方法都去引用\n\n`@Before`：前置增强\n\n`@AfterReturning`：后置增强，其中的returning的值必须和方法传入的参数名相同\n\n`@Around`：环绕增强\n\n`@AfterThrowing`：异常抛出增强，其中的throwing的值必须和方法传入的参数名相同\n\n`@After`：最终增强\n\n```java\n/**\n * ProductDao的增强类(切面类)\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@Aspect\npublic class ProductEnhancer {\n\n    /**\n     * 切入点配置\n     * 对ProductDaoImpl里的方法都增强\n     */\n    @Pointcut(value = \"execution(* learningspring.aop.aspectj.annotation.demo2.ProductDaoImpl.*(..))\")\n    private void pointcut1(){}\n\n    /**\n     * 前置增强案例\n     * 在调用save方法之前进行权限校验\n     * @param joinPoint 切入点对象\n     */\n    @Before(value = \"execution(* learningspring.aop.aspectj.annotation.demo2.ProductDaoImpl.save())\")\n    public void checkPri(JoinPoint joinPoint){\n        System.out.println(\"【前置增强】权限校验\" + joinPoint);\n    }\n\n    /**\n     * 后置增强案例\n     * 在调用delete方法之后，写入日志记录操作时间\n     * @param result 目标方法返回的对象\n     */\n    @AfterReturning(returning = \"result\", value = \"execution(* learningspring.aop.aspectj.annotation.demo2.ProductDaoImpl.delete())\")\n    public void writeLog(Object result){\n        System.out.println(\"【后置增强】写入日志 操作时间：\" + result.toString());\n    }\n\n    /**\n     * 环绕增强\n     * 在调用modify方法前后，显示性能参数\n     * @param joinPoint 切入点对象\n     * @throws Throwable 可抛出的异常\n     */\n    @Around(value = \"execution(* learningspring.aop.aspectj.annotation.demo2.ProductDaoImpl.modify())\")\n    public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable{\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        Object obj = joinPoint.proceed();\n        System.out.println(\"【环绕增强】当前空闲内存\" + Runtime.getRuntime().freeMemory()/(1024 * 1024) + \"MB\");\n        return obj;\n    }\n\n    /**\n     * 异常抛出增强\n     * 在调用query时若抛出异常则打印异常信息\n     * @param ex 异常对象\n     */\n    @AfterThrowing(throwing = \"ex\", value = \"execution(* learningspring.aop.aspectj.annotation.demo2.ProductDaoImpl.query())\")\n    public void exception(Throwable ex){\n        System.out.println(\"【异常抛出增强】\" + \"异常信息：\" +ex.getMessage());\n    }\n\n    /**\n     * 最终增强\n     * 无论ProductDaoImpl里的每个方法是否抛出异常都打印当前时间\n     */\n    @After(value = \"pointcut1()\")\n    public void finallyAdvice(){\n        System.out.println(\"【最终增强】\" + new Date().toString());\n    }\n}\n\n```\n\n编写测试方法\n\n```java\n/**\n * AspectJ的注解方式配置测试类\n *\n * @author Chen Rui\n * @version 1.0\n **/\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(\"classpath:aspectj-annotation.xml\")\npublic class AppTest {\n\n    @Resource(name = \"productDao\")\n    private ProductDao productDao;\n\n    @Test\n    public void test(){\n\n        productDao.save();\n\n        productDao.delete();\n\n        productDao.modify();\n\n        productDao.query();\n    }\n}\n```\n\n运行，控制台输出\n\n```\n【前置增强】权限校验execution(void learningspring.aop.aspectj.annotation.demo2.ProductDao.save())\n添加商品\n【最终增强】Tue Mar 19 16:01:06 CST 2019\n删除商品\n【最终增强】Tue Mar 19 16:01:06 CST 2019\n【后置增强】写入日志 操作时间：Tue Mar 19 16:01:06 CST 2019\n【环绕增强】当前空闲内存186MB\n修改商品\n【环绕增强】当前空闲内存186MB\n【最终增强】Tue Mar 19 16:01:06 CST 2019\n查询商品\n【最终增强】Tue Mar 19 16:01:06 CST 2019\n【异常抛出增强】异常信息：/ by zero\n```\n\n# Spring JDBC Template\n\nSpring提供了提供了多种持久层技术的模板类\n\n| ORM持久化技术   | 模板类                                               |\n| --------------- | ---------------------------------------------------- |\n| JDBC            | org.springframework.jdbc.core.JdbcTemplate           |\n| Hibernate3.0    | org.springframework.orm.hibernate3.HibernateTemplate |\n| IBatis(Mybatis) | org.springframework.orm.ibatis.SqlMapClientTemplate  |\n| JPA             | org.springframework.orm.jpa.JpaTemplate              |\n\n## JDBC Template的入门\n\n首先引入jar包，在`pom.xml`文件中加入`spring-jdbc`，`spring-tx`，`mysql-connector-java`（本案例使用的是MySQL8）三个依赖。\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework\u003c/groupId\u003e\n    \u003cartifactId\u003espring-jdbc\u003c/artifactId\u003e\n    \u003cversion\u003e4.3.14.RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework\u003c/groupId\u003e\n    \u003cartifactId\u003espring-tx\u003c/artifactId\u003e\n    \u003cversion\u003e4.3.14.RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003cdependency\u003e\n    \u003cgroupId\u003emysql\u003c/groupId\u003e\n    \u003cartifactId\u003emysql-connector-java\u003c/artifactId\u003e\n    \u003cversion\u003e8.0.15\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n然后创建数据库表，本例使用的MySQL8\n\n```mysql\ncreate table account\n(\n\tid int auto_increment\n\t\tprimary key,\n\tname varchar(8) not null,\n\tmoney double default 0\n)\ncomment '账户表';\n```\n\n### 基本使用\n\n最基本的使用，不依赖于Spring 的管理，手动创建对象，采用硬编码的方式进行属性注入。不推荐使用该方法。\n\n```java\npublic class AppTest {\n    /**\n     * 硬编码\n     */\n    @Test\n    public void test1(){\n        // 创建连接池\n        DriverManagerDataSource dataSource = new DriverManagerDataSource();\n        dataSource.setDriverClassName(\"com.mysql.cj.jdbc.Driver\");\n        dataSource.setUrl(\"jdbc:mysql://localhost:3306/springjdbc?useUnicode=true\u0026characterEncoding=utf-8\u0026serverTimezone=Asia/Shanghai\u0026useSSL=false \");\n        dataSource.setUsername(\"root\");\n        dataSource.setPassword(\"123456\");\n        // 创建JDBC Template\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        int i = jdbcTemplate.update(\"INSERT INTO account VALUES (null ,?,?)\", \"Tom\", 20000d);\n        if (i \u003e 0){\n            System.out.println(\"Update Successful\");\n        }\n    }\n}\n```\n\n接下来使用第二种方法，把连接池对象和模板(Template)都交给Spring来管理\n\n创建`spring-jdbc.xml`该文件用来管理Bean\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\u003e\n\n    \u003c!-- 配置数据库连接池 --\u003e\n    \u003cbean id=\"dataSource\" class=\"org.springframework.jdbc.datasource.DriverManagerDataSource\"\u003e\n        \u003cproperty name=\"driverClassName\" value=\"com.mysql.cj.jdbc.Driver\"/\u003e\n        \u003cproperty name=\"url\" value=\"jdbc:mysql://localhost:3306/springjdbc?useUnicode=true\u0026amp;characterEncoding=utf-8\u0026amp;serverTimezone=Asia/Shanghai\u0026amp;useSSL=false\"/\u003e\n        \u003cproperty name=\"username\" value=\"root\"/\u003e\n        \u003cproperty name=\"password\" value=\"123456\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置Spring JDBC Template --\u003e\n    \u003cbean id=\"jdbcTemplate\" class=\"org.springframework.jdbc.core.JdbcTemplate\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\n\u003c/beans\u003e\n```\n\n在测试类中加入相应的注解，以及配置文件信息，编写新的测试方法\n\n```java\n/**\n * Spring JDBC Template的使用\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(\"classpath:spring-jdbc.xml\")\npublic class AppTest {\n    /**\n     * Spring 配置文件方式\n     * 把连接池和模板(Template)都交给spring管理\n     * 日志信息：Loaded JDBC driver: com.mysql.cj.jdbc.Driver\n     * 是使用的默认的连接池\n     */\n    @Resource(name = \"jdbcTemplate\")\n    private JdbcTemplate jdbcTemplate;\n\n    @Test\n    public void test2(){\n        int i = jdbcTemplate.update(\"INSERT INTO account VALUES (null ,?,?)\", \"Jack\", 30000d);\n        if (i \u003e 0){\n            System.out.println(\"Update Successful\");\n        }\n    }\n```\n\n通过`@Resource`注解从IOC容器中获取到模板对象，然后通过该模板对象来操作数据库。\n\n这样就完成了Spring JDBC Template的最基本使用\n\n### 数据库连接池\n\n在实际开发中，可能并不会使用默认的连接池，而是去使用一些开源的数据库连接池，在该例中介绍两种数据库连接池DBCP和C3P0\n\n#### DBCP连接池的配置\n\n首先创建连接数据库的配置文件`db.properties`，注意，不同的MySQL版本可能url信息会不同，比如MySQL8就需要添加`serverTimezone`参数。\n\n```properties\njdbc.driverClassName=com.mysql.cj.jdbc.Driver\njdbc.url=jdbc:mysql://localhost:3306/springjdbc?useUnicode=true\u0026characterEncoding=utf-8\u0026serverTimezone=Asia/Shanghai\u0026useSSL=false \njdbc.username=root\njdbc.password=123456\n```\n\n接着创建一个新的配置文件`spring-dbcp.xml`和前面的配置文件做区分。\n\n利用`context:property-placeholder`标签引入`db.properties`配置文件，通过`${key}`的方式来获取对应的value。\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       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\"\u003e\n\n    \u003c!-- 引入数据库配置文件 --\u003e\n    \u003ccontext:property-placeholder location=\"db.properties\"/\u003e\n\n    \u003c!-- 配置DBCP连接池 --\u003e\n    \u003cbean id=\"dataSource\" class=\"org.apache.commons.dbcp2.BasicDataSource\"\u003e\n        \u003cproperty name=\"driverClassName\" value=\"${jdbc.driverClassName}\"/\u003e\n        \u003cproperty name=\"url\" value=\"${jdbc.url}\"/\u003e\n        \u003cproperty name=\"username\" value=\"${jdbc.username}\"/\u003e\n        \u003cproperty name=\"password\" value=\"${jdbc.password}\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置Spring JDBC Template --\u003e\n    \u003cbean id=\"jdbcTemplateDBCP\" class=\"org.springframework.jdbc.core.JdbcTemplate\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n编写测试方法\n\n```java\n/**\n * Spring JDBC Template的使用\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(\"classpath:spring-dbcp.xml\")\npublic class AppTest {\n    //使用开源的数据库连接池进行配置\n\n    /**\n     * 使用DBCP连接池\n     */\n    @Resource(name = \"jdbcTemplateDBCP\")\n    private JdbcTemplate jdbcTemplateDBCP;\n\n    @Test\n    public void test3(){\n        int i = jdbcTemplateDBCP.update(\"INSERT INTO account VALUES (null ,?,?)\", \"Lucy\", 40000d);\n        if (i \u003e 0){\n            System.out.println(\"Update Successful\");\n        }\n    }\n}\n```\n\n#### C3P0连接池配置\n\n同样是创建一个新的配置文件`spring-c3p0.xml`，以作区分，同时也要引入数据库配置文件`db.properties`\n\n要注意`property`标签的`name`属性和前面的配置文件**稍有不同**\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans.xsd\n\thttp://www.springframework.org/schema/context\n\thttp://www.springframework.org/schema/context/spring-context.xsd\"\u003e\n\n    \u003c!-- 引入数据库配置文件 --\u003e\n    \u003ccontext:property-placeholder location=\"db.properties\"/\u003e\n\n    \u003c!-- 配置C3P0连接池 --\u003e\n    \u003cbean id=\"dataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\u003e\n        \u003cproperty name=\"driverClass\" value=\"${jdbc.driverClassName}\"/\u003e\n        \u003cproperty name=\"jdbcUrl\" value=\"${jdbc.url}\"/\u003e\n        \u003cproperty name=\"user\" value=\"${jdbc.username}\"/\u003e\n        \u003cproperty name=\"password\" value=\"${jdbc.password}\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置Spring JDBC Template --\u003e\n    \u003cbean id=\"jdbcTemplateC3P0\" class=\"org.springframework.jdbc.core.JdbcTemplate\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n编写测试方法\n\n```java\n/**\n * Spring JDBC Template的使用\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(\"classpath:spring-*.xml\")\npublic class AppTest {\n    //使用开源的数据库连接池进行配置\n\n    /**\n     * 使用C3P0连接池\n     */\n    @Resource(name = \"jdbcTemplateC3P0\")\n    private JdbcTemplate jdbcTemplateC3P0;\n\n    @Test\n    public void test4(){\n        int i = jdbcTemplateC3P0.update(\"INSERT INTO account VALUES (null ,?,?)\", \"Lily\", 50000d);\n        if (i \u003e 0){\n            System.out.println(\"Update Successful\");\n        }\n    }\n}\n\n```\n\n### 完成基本的CRUD操作\n\n以下内容都是使用的**C3P0连接池**，并且通过`@Resource`注解从IOC容器中获取了`jdbcTemplateC3P0`对象\n\n#### 插入操作\n\n```java\n/**\n * 插入操作\n */\n@Test\npublic void test(){\n    int i = jdbcTemplateC3P0.update(\"INSERT INTO account VALUES (null ,?,?)\", \"Lily\", 50000d);\n    if (i \u003e 0){\n        System.out.println(\"Update Successful\");\n    }\n}\n```\n\n#### 修改操作\n\n```java\n/**\n * 修改操作\n */\n@Test\npublic void test(){\n    int i = jdbcTemplateC3P0.update(\"UPDATE account SET name = ? WHERE id = ?\", \"Bob\", 1);\n    if (i \u003e 0){\n        System.out.println(\"Update Successful\");\n    }\n}\n```\n\n#### 删除操作\n\n```java\n/**\n * 删除操作\n */\n@Test\npublic void test(){\n    int i = jdbcTemplateC3P0.update(\"DELETE FROM account WHERE id = ?\", 2);\n    if (i \u003e 0){\n        System.out.println(\"Delete Successful\");\n    }\n}\n```\n\n#### 查询操作\n\n##### 查询某个属性\n\n```java\n/**\n * 查询操作\n *\n * 查询单个字符串结果\n */\n@Test\npublic void test(){\n    String result = jdbcTemplateC3P0.queryForObject(\"SELECT name FROM account WHERE id = ?\", String.class, 1);\n    if (result != null){\n        System.out.println(result);\n    } else{\n        System.out.println(\"NULL\");\n    }\n}\n\n/**\n * 统计查询\n * 返回数据表中的记录数\n */\n@Test\npublic void test(){\n    Long result = jdbcTemplateC3P0.queryForObject(\"SELECT COUNT(*) FROM account\", Long.class);\n    System.out.println(result);\n}\n```\n\n##### 查询返回单个对象\n\n要实现查询的数据封装成一个对象的话，查询`queryForObject`的参数列表可知需要一个`rowMapper`的参数。所以需要创建一个执行数据封装的类来实现`RowMapper\u003cT\u003e`接口里的`mapRow`方法，在这个方法里进行数据对象的封装。\n\n```java\n/**\n * 数据封装类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class MyRowMapper implements RowMapper\u003cAccount\u003e {\n    @Override\n    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {\n        Account account = new Account();\n        account.setId(rs.getInt(\"id\"));\n        account.setName(rs.getString(\"name\"));\n        account.setMoney(rs.getDouble(\"money\"));\n        return account;\n    }\n}\n\n```\n\n编写测试方法\n\n```java\n/**\n * 将查询的结果封装成对象\n * 要创建一个自定义rowMapper来实现RowMapper接口\n */\n@Test\npublic void test(){\n    Account account = jdbcTemplateC3P0.queryForObject(\"SELECT * FROM account WHERE id = ?\", new MyRowMapper(), 1);\n    if (account != null){\n        System.out.println(account);\n    } else{\n        System.out.println(\"NULL\");\n    }\n}\n```\n\n##### 查询返回对象集合\n\n要实现查询返回对象集合依然需要自定义类实现`RowMapper\u003cT\u003e`接口，调用的是`query`方法\n\n```java\n/**\n * 查询多条记录\n */\n@Test\npublic void test10(){\n    List\u003cAccount\u003e accounts = jdbcTemplateC3P0.query(\"SELECT * FROM account\", new MyRowMapper());\n    accounts.forEach(System.out::println);\n}\n```\n\n# Spring事务管理\n\n## 什么是事务\n\n事务：逻辑上的一组操作，组成这组操作的各个单元，要么全部成功，要么全部失败。\n\n## 事务的特性\n\n+ 原子性：事务不可分割\n+ 一致性：事务执行前后数据完整性保持一致\n+ 隔离性：一个事务的执行不应该受到其他事务的干扰\n+ 持久性：一旦事务结束，数据就持久化到数据库\n\n## 不考虑隔离性引发的安全性问题\n\n+ 读问题\n  + 脏读：A事务读到B事务未提交的数据\n  + 不可重复读：B事务在A事务两次读取数据之间，修改了数据，导致A事务两次读取结果不一致\n  + 幻读/虚读：B事务在A事务批量修改数据时，插入了一条新的数据，导致数据库中仍有一条数据未被修改。\n+ 写问题\n  + 丢失更新：\n\n## 解决读问题\n\n+ 设置事务的隔离级别\n  + `Read uncommitted`：未提交读，任何读问题都解决不了\n  + `Read committed`：已提交读，解决脏读，但是不可重复读和幻读有可能发生\n  + `Repeatable read`：重复读，解决脏读和不可重复读，但是幻读有可能发生\n  + `Serializable`：解决所有读问题，因为禁止并行执行\n\n## Spring事务管理API\n\n+ `PlatformTransactionManager`：平台事务管理器\n\n  + `DataSourceTransactionManager`：底层使用JDBC管理事务\n\n+ `TransactionDefinition`：事务定义信息\n\n  ​\t用于定义事务相关的信息，隔离级别，超时信息，传播行为，是否只读……\n\n+ `TransactionStatus`：事务的状态\n\n  ​\t用于记录在事务管理过程中，事务的状态\n\nAPI的关系：\n\nSpring在进行事务管理的时候，首先**平台事务管理器**根据**事务定义信息**进行事务的管理，在事务管理过程中，产生各种状态，将这些状态信息记录到**事务状态对象**\n\n## Spring事务的传播行为\n\n首先假设一个背景，Service1里的x()方法已经定义了一个事务，Service2里的y()方法也有一个事务，但现在新增一行代码在Service2的y()方法中要先调用Service1里的x()方法然后再执行本身的方法。这时就涉及到**事务的传播行为**。\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321110709.png)\n\nSpring中提供了7种传播行为\n\n**假设x()方法称为A，y()方法称为B**\n\n+ 保证多个操作在同一个事务中\n  + **`PROPAGATION_REQUIRED`**(\\*)：Spring事务隔离级别的默认值。如果A中有事务，则使用A中的事务。如果没有，则创建一个新的事务，将操作包含进来。\n  + `PROPAGATION_SUPPORTS`：支持事务。如果A中有事务，使用A中的事务。如果A没有事务，则不使用事务。\n  + `PROPAGATION_MANDATORY`：如果A中有事务，使用A中的事务。如果没有事务，则抛出异常。\n+ 保证多个事务不在同一个事务中\n  + **`PROPAGATION_REQUIRES_NEW`**(\\*)：如果A中有事务，将A的事务挂起，创建新事务，只包含自身操作。如果A中没有事务，创建一个新事物，包含自身操作。\n  + `PROPAGATION_NOT_SUPPORTED`：如果A中有事务，将A的事务挂起，不使用事务。\n  + `PROPAGATION_NEVER`：如果A中有事务，则抛出异常。\n+ 嵌套式事务\n  + **`PROPAGATION_NESTED`**(\\*)：嵌套事务，如果A中有事务，则按照A的事务执行，执行完成后，设置一个保存点，再执行B中的操作，如果无异常，则执行通过，如果有异常，则可以选择回滚到初始位置或者保存点。\n\n## Spring事务管理案例——转账情景\n\n### 转账情景实现\n\n首先创建接口`AccountDao`，定义两个方法分别是`out`和`in`\n\n```java\n/**\n * AccountDao\n *\n * @author Chen Rui\n * @version 1.0\n **/\n\npublic interface AccountDao {\n\n    /**\n     * 转出\n     *\n     * @param from  转出账户\n     * @param money 转出金额\n     */\n    void out(String from, double money);\n\n    /**\n     * 转入\n     *\n     * @param to    转入账户\n     * @param money 转入金额\n     */\n    void in(String to, double money);\n}\n```\n\n接着创建实现类`AccountDaoImpl`实现`out`和`in`方法并且继承`JdbcSupport`类。这样就可以直接使用父类的`JDBCTemplate`对象。\n\n```java\n/**\n * AccountDao实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {\n\n    @Override\n    public void out(String from, double money) {\n        this.getJdbcTemplate().update(\"UPDATE account SET money = money - ? WHERE name = ?\", money, from);\n    }\n\n    @Override\n    public void in(String to, double money) {\n        this.getJdbcTemplate().update(\"UPDATE account SET money = money + ? WHERE name = ?\", money, to);\n    }\n}\n\n```\n\n然后创建接口`AccountrService`，定义`transfer`方法\n\n```java\n/**\n * AccountService\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic interface AccountService {\n\n    /**\n     * 转账\n     * @param from 转出账户\n     * @param to 转入账户\n     * @param money 交易金额\n     */\n    void transfer(String from, String to, Double money);\n}\n\n```\n\n再创建类`AccountServiceImpl`实现该接口，并声明`AccountDao`引用并创建`set`方法\n\n```java\n/**\n * AccountService实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AccountServiceImpl implements AccountService {\n\n    private AccountDao accountDao;\n\n    public void setAccountDao(AccountDao accountDao) {\n        this.accountDao = accountDao;\n    }\n\n    @Override\n    public void transfer(String from, String to, Double money) {\n        accountDao.out(from, money);\n        accountDao.in(to, money);\n    }\n}\n```\n\n最后创建配置文件`spring-tx-programmatic.xml`，用来管理Bean。\n\n引入数据库连接文件，配置数据源，创建Bean对象`accountDao`将数据源`dataSource`注入到`accountDao`中，再创建Bean对象`accountService`，将`accountDao`注入。\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"                                             \n            http://www.springframework.org/schema/beans  \n            http://www.springframework.org/schema/beans/spring-beans.xsd  \n            http://www.springframework.org/schema/context   \n            http://www.springframework.org/schema/context/spring-context.xsd  \n            http://www.springframework.org/schema/tx \n            http://www.springframework.org/schema/tx/spring-tx.xsd\n            http://www.springframework.org/schema/aop\n            http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 编程式事务管理配置文件 --\u003e\n\n    \u003c!-- 配置Service --\u003e\n    \u003cbean id=\"accountService\" class=\"learningspring.transaction.programmatic.AccountServiceImpl\"\u003e\n        \u003cproperty name=\"accountDao\" ref=\"accountDao\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置Dao --\u003e\n    \u003cbean id=\"accountDao\" class=\"learningspring.transaction.programmatic.AccountDaoImpl\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 引入数据库配置文件 --\u003e\n    \u003ccontext:property-placeholder location=\"db.properties\"/\u003e\n\n    \u003c!-- 配置C3P0连接池 --\u003e\n    \u003cbean id=\"dataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\u003e\n        \u003cproperty name=\"driverClass\" value=\"${jdbc.driverClassName}\"/\u003e\n        \u003cproperty name=\"jdbcUrl\" value=\"${jdbc.url}\"/\u003e\n        \u003cproperty name=\"user\" value=\"${jdbc.username}\"/\u003e\n        \u003cproperty name=\"password\" value=\"${jdbc.password}\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n到此一个转账模拟业务就实现了，数据库表依然使用前面创建的`account`表，先查询当前数据库的数据。\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321124514.png)\n\n编写测试方法，实现让姓名为Bob的账户向Jack转账1000元。\n\n```java\n/**\n * 编程式事务测试类\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(value = \"classpath:spring-tx-programmatic.xml\")\npublic class AppTest {\n\n    @Resource(name = \"accountService\")\n    private AccountService accountService;\n\n    @Test\n    public void test(){\n        accountService.transfer(\"Bob\",\"Jack\",1000d);\n    }\n}\n```\n\n运行结果\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321124630.png)\n\n现在对类`AccountServiceImpl`里的`transfer`方法进行修改，让其发生异常，再观察结果\n\n```java\n/**\n * AccountService实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AccountServiceImpl implements AccountService {\n\n    private AccountDao accountDao;\n\n    public void setAccountDao(AccountDao accountDao) {\n        this.accountDao = accountDao;\n    }\n\n    @Override\n    public void transfer(String from, String to, Double money) {\n        accountDao.out(from, money);\n        // 抛出异常\n        int i = 1/0;\n        accountDao.in(to, money);\n    }\n\n}\n\n```\n\n查询数据库数据\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321125027.png)\n\n这时Bob账户的钱就少了1000元，而Jack账户也没有增加1000元。\n\n所以就需要事务来进行管理。\n\n### 编程式事务\n\n所谓编程式事务，就是要在源码中编写事务相关的代码。实现编程式事务，首先要在`AccountServiceImpl`中声明`TransactionTemplate`对象，并创建set方法。然后修改`transfer`参数列表所有参数都用`final`(因为使用了匿名内部类)修饰，并修改方法体内容。\n\n```java\n/**\n * AccountService实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AccountServiceImpl implements AccountService {\n\n    private AccountDao accountDao;\n\n    private TransactionTemplate transactionTemplate;\n\n    public void setAccountDao(AccountDao accountDao) {\n        this.accountDao = accountDao;\n    }\n    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {\n        this.transactionTemplate = transactionTemplate;\n    }\n\n    @Override\n    public void transfer(final String from, final String to, final Double money) {\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n            @Override\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n                accountDao.out(from, money);\n                // 抛出异常\n                int i = 1/0;\n                accountDao.in(to,money);\n            }\n        });\n    }\n}\n```\n\n然后修改`spring-tx-programmatic.xml`文件，创建Bean对象`transactionManager`和`transactionTemplate`，并将`transactionTemplate`注入到`accountService`中。\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"                                             \n            http://www.springframework.org/schema/beans  \n            http://www.springframework.org/schema/beans/spring-beans.xsd  \n            http://www.springframework.org/schema/context   \n            http://www.springframework.org/schema/context/spring-context.xsd  \n            http://www.springframework.org/schema/tx \n            http://www.springframework.org/schema/tx/spring-tx.xsd\n            http://www.springframework.org/schema/aop\n            http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 编程式事务管理配置文件 --\u003e\n\n    \u003c!-- 配置Service --\u003e\n    \u003cbean id=\"accountService\" class=\"learningspring.transaction.programmatic.AccountServiceImpl\"\u003e\n        \u003cproperty name=\"accountDao\" ref=\"accountDao\"/\u003e\n        \u003cproperty name=\"transactionTemplate\" ref=\"transactionTemplate\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置Dao --\u003e\n    \u003cbean id=\"accountDao\" class=\"learningspring.transaction.programmatic.AccountDaoImpl\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 引入数据库配置文件 --\u003e\n    \u003ccontext:property-placeholder location=\"db.properties\"/\u003e\n\n    \u003c!-- 配置C3P0连接池 --\u003e\n    \u003cbean id=\"dataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\u003e\n        \u003cproperty name=\"driverClass\" value=\"${jdbc.driverClassName}\"/\u003e\n        \u003cproperty name=\"jdbcUrl\" value=\"${jdbc.url}\"/\u003e\n        \u003cproperty name=\"user\" value=\"${jdbc.username}\"/\u003e\n        \u003cproperty name=\"password\" value=\"${jdbc.password}\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置事务管理器 --\u003e\n    \u003cbean id=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置模板 --\u003e\n    \u003cbean id=\"transactionTemplate\" class=\"org.springframework.transaction.support.TransactionTemplate\"\u003e\n        \u003cproperty name=\"transactionManager\" ref=\"transactionManager\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n此时异常依然存在，数据库数据仍然是上次执行的结果状态\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321125027.png)\n\n再次运行测试方法，并查询结果，观察是否发生变化\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321130039.png)\n\n现在就实现了编程式事务，当出现异常时，数据库的数据就不会被修改。\n\n### 声明式事务\n\n#### XML配置方式\n\n声明式事务即通过配置文件实现，利用的就是Spring的AOP\n\n修改类`AccountServiceImpl`，删除`TransactionTemplate`对象，并修改`transfer`方法，保留异常代码\n\n```java\n/**\n * AccountService实现类\n *\n * @author Chen Rui\n * @version 1.0\n **/\npublic class AccountServiceImpl implements AccountService{\n\n    private AccountDao accountDao;\n\n    public void setAccountDao(AccountDao accountDao) {\n        this.accountDao = accountDao;\n    }\n\n    @Override\n    public void transfer(String from, String to, Double money) {\n        accountDao.out(from, money);\n        int i = 1/0;\n        accountDao.in(to,money);\n\n    }\n}\n```\n\n然后创建配置文件`spring-tx-declarative.xml`，配置数据源即Bean对象，然后配置事务管理器。\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"\n            http://www.springframework.org/schema/beans\n            http://www.springframework.org/schema/beans/spring-beans.xsd\n            http://www.springframework.org/schema/context\n            http://www.springframework.org/schema/context/spring-context.xsd\n            http://www.springframework.org/schema/tx\n            http://www.springframework.org/schema/tx/spring-tx.xsd\n            http://www.springframework.org/schema/aop\n            http://www.springframework.org/schema/aop/spring-aop.xsd\"\u003e\n\n    \u003c!-- 声明式事务管理配置文件 --\u003e\n\n    \u003c!-- 配置Service --\u003e\n    \u003cbean id=\"accountService\" class=\"learningspring.transaction.declarative.AccountServiceImpl\"\u003e\n        \u003cproperty name=\"accountDao\" ref=\"accountDao\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 配置Dao --\u003e\n    \u003cbean id=\"accountDao\" class=\"learningspring.transaction.declarative.AccountDaoImpl\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 引入数据库配置文件 --\u003e\n    \u003ccontext:property-placeholder location=\"db.properties\"/\u003e\n\n    \u003c!-- 配置C3P0连接池 --\u003e\n    \u003cbean id=\"dataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\u003e\n        \u003cproperty name=\"driverClass\" value=\"${jdbc.driverClassName}\"/\u003e\n        \u003cproperty name=\"jdbcUrl\" value=\"${jdbc.url}\"/\u003e\n        \u003cproperty name=\"user\" value=\"${jdbc.username}\"/\u003e\n        \u003cproperty name=\"password\" value=\"${jdbc.password}\"/\u003e\n    \u003c/bean\u003e\n    \n    \u003c!-- 配置事务管理器 --\u003e\n    \u003cbean id=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\u003c/beans\u003e\n```\n\n接着就配置事务的增强，配置文件中加入以下配置\n\n```xml\n\u003c!-- 配置事务的增强 --\u003e\n\u003ctx:advice id=\"txAdvice\" transaction-manager=\"transactionManager\"\u003e\n    \u003ctx:attributes\u003e\n        \u003c!-- 配置事务的规则 根据实际业务修改--\u003e\n        \u003ctx:method name=\"*\" propagation=\"REQUIRED\"/\u003e\n    \u003c/tx:attributes\u003e\n\u003c/tx:advice\u003e\n\n\u003c!-- AOP的配置 --\u003e\n\u003caop:config\u003e\n    \u003caop:pointcut id=\"pointcut1\" expression=\"execution(* learningspring.transaction.declarative.AccountServiceImpl.*(..))\"/\u003e\n    \u003caop:advisor advice-ref=\"txAdvice\" pointcut-ref=\"pointcut1\"/\u003e\n\u003c/aop:config\u003e\n```\n\n先查看当前数据库数据\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321130039.png)\n\n编写测试方法\n\n```java\n/**\n * 声明式事务配置测试类\n *\n * @author Chen Rui\n * @version 1.0\n **/\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(value = \"classpath:spring-tx-declarative.xml\")\npublic class AppTest {\n\n    @Resource(name = \"accountService\")\n    private AccountService accountService;\n\n    @Test\n    public void test(){\n        accountService.transfer(\"Bob\",\"Jack\",1000d);\n    }\n}\n```\n\n运行查看结果，是否变化\n\n![](https://blogpictrue-1251547651.cos.ap-chengdu.myqcloud.com/blog/20190321132512.png)\n\n至此就实现了声明式事务XML方式的配置。\n\n#### 注解配置方式\n\nSpring的事务配置仍然支持注解配置\n\n继续沿用`spring-tx-declarative.xml`文件，把事务增强和AOP相关的配置注释，并开启注解事务。\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cbeans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:tx=\"http://www.springframework.org/schema/tx\"\n       xmlns:aop=\"http://www.springframework.org/schema/aop\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-ins","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmozhu811%2Flearning-spring","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmozhu811%2Flearning-spring","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmozhu811%2Flearning-spring/lists"}