spring框架

spring

概述

	Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架

spring的优势

1. 方便解耦,简化开发: 主要就是IoC容器
2. AOP编程的支持: 面向切面的编程
3. 声明式事务的支持
4. 方便程序的测试
5. 方便集成各种优秀框架
6. 降低 JavaEE API的使用难度
7. Java源码是经典学习范例

spring体系结构

spring框架

springIoC容器(控制反转)

概念和作用

	控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
	
	作用: 降低程序间的依赖关系(耦合).

高内聚低耦合

内聚: 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展, 内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系.
	讲究的就是在一个类中的各个元素是紧密相连的

耦合: 耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。
	讲究的是在类与类之间的依赖关系不紧密

	耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须 存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。 

工厂解耦的方式

解耦的思路

第一步: 使用反射来创建对象, 而避免使用new关键字
第二步: 通过读取配文件来获取创建对象权限定类名
第三步: 为了避免重复创建, 我们将创建的对象放入容器(Map集合)中, 
			工厂就是负责给我们从容器中获取指定对象的类

工厂设计模式 + 反射 + 配置文件 进行解耦

	在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的
方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。 
	那么,这个读取配置文件,创建和获取三层对象的类就是工厂。 

使用 springIOC解决程序耦合

第一步: pom.xml中配置spring的依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.0.2.RELEASE</version>
</dependency>

第二步:在类的根路径下创建一个任意名称的 xml 文件

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
   					http://www.springframework.org/schema/beans/spring-beans.xsd"> 
</beans>

第三步:让 spring 管理资源,在配置文件中配置 service 和 dao

<!-- bean 标签:用于配置让 spring 创建对象,并且存入 ioc 容器之中       
	id 属性:对象的唯一标识。       
	class 属性:指定要创建对象的全限定类名 
--> 
	<!-- 配置 service -->    
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>
	<!-- 配置 dao --> 
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"/>

测试配置是否成功

public class Client {  
    /** 
      * 使用 main 方法获取容器测试执行 
      */  
    public static void main(String[] args) {   
        //1.使用 ApplicationContext 接口,就是在获取 spring 容器   
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");   
        //2.根据 bean 的 id 获取对象   
        IAccountService aService = (IAccountService) ac.getBean("accountService");   
        System.out.println(aService);   
 
        IAccountDao aDao = (IAccountDao) ac.getBean("accountDao");  
        System.out.println(aDao);  } } 

BeanFactory和 ApplicationContext 的区别

BeanFactory 才是 Spring 容器中的顶层接口。 
ApplicationContext 是它的子接口。 

BeanFactory 和 ApplicationContext 的区别:  
	创建对象的时间点不一样。   
		ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。   
		BeanFactory:什么使用什么时候创建对象。

ApplicationContext 接口的实现类

三种容器区别

ClassPathXmlApplicationContext:  
它是从类的根路径下加载配置文件  						推荐使用这种 

FileSystemXmlApplicationContext:  
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。 

AnnotationConfigApplicationContext: 
当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。 

实例化 Bean 的三种方式

1. 使用默认无参构造函数					推荐使用这种
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/> 

2. spring管理静态工厂-使用静态工厂的方法创建对象
<bean id="accountService" 
    class="com.itheima.factory.StaticFactory" factory-method="createAccountService"></bean> 

3. spring管理实例工厂-使用实例工厂的方法创建对象
<bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean>  
<bean id="accountService" factory-bean="instancFactory" factory-method="createAccountService"></bean>

bean的作用范围

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="singleton"></bean>

单例: singleton		默认就是单例
多例: prototype

bean的生命周期

单例对象:scope="singleton"   
	一个应用只有一个对象的实例。它的作用范围就是整个引用。   
		生命周期:    
			对象出生:当应用加载,创建容器时,对象就被创建了。    
			对象活着:只要容器在,对象一直活着。    
			对象死亡:当应用卸载,销毁容器时,对象就被销毁了。  
多例对象:scope="prototype"   
	每次访问对象时,都会重新创建对象实例。   
		生命周期:    
			对象出生:当使用对象时,创建新的对象实例。    
			对象活着:只要对象在使用中,就一直活着。    
			对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。 

springDI(依赖注入)

概念

依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现.

IoC的作用
	降低程序间的耦合(依赖注入)
依赖关系的管理:
	由spring管理
	在当前类中想要获取其他类的对象时, 由spring为我们提供, 我们只需要在配置文件中说明
依赖关系的维护:
	就称之为依赖注入

注入的数据类型

基本数据类型 和 String
bean对象类型(在配置文件或注解中配置过的bean)
Array List, Set(去重复的元素), Map, Properties集合类型

注入方式

1.构造方法注入

使用的标签: constructor-arg
标签出现的位置: bean标签的内部
标签中的属性
	type: 
	index:
	name: 用于指定给构造函数中指定名称的参数
	value: 用于提供及基本类型和String类型的数据
	ref: 用于指定其他的bean类型. 它指的就是在spring的IoC核心容器中出现过的bean对象
优势:
	在获取bean对象时: 注入数据是必须的操作, 否则对象无法创建
弊端:
	改变了bean对象的实例化方式, 使我们在创建对象时, 如果用不到这些数据, 也必须提供.

<bean id="accountService" class="cn.fang.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="age" value="18"/>
        <constructor-arg name="birthday" ref="new"/>
</bean>
<!-- 配置一个日期对象 -->
<bean id="new" class="java.util.Date"/>

2.set方法注入 (常用)

使用的标签: property
出现的位置: bean标签的内部
标签的属性:
	name: 用于指定注入时所调用的set方法名称
	value: 用于提供及基本类型和String类型的数据
	ref: 用于指定其他的bean类型. 它指的就是在spring的IoC核心容器中出现过的bean对象
优势:
	创建对象时没有明确的限制, 可以直接使用默认构造函数.
弊端:
	如果某个成员必须有值, 则获得对象是可能set方法没有执行.

<bean id="accountService2" class="cn.fang.service.impl.AccountServiceImpl2">
        <property name="name" value="张三"></property>
        <property name="age" value="14"></property>
        <property name="birthday" ref="new"></property>
</bean>
<!-- 配置一个日期对象 -->
<bean id="new" class="java.util.Date"/>


复杂类型的注入/集合类型的注入
Array/List<T>/Set<T>类型
标签的属性:	
	name: 对应的类型
	name为set是可以取出重复元素
list标签 | array标签 | set标签 可以互换
<property name="set">
	<list>
		<value>AAAA</value>
		<value>BBBB</value>
		<value>BBBB</value>
		<value>CCCC</value>
		<value>AAAA</value>
	</list>
</property>

Map<T,M>/Properties类型
标签的属性:
	name: 对应的类型
map标签 | properties标签 可以互换
子标签entry的属性:
	key: 就是Map集合中的键
	value: 就是Map集合中的值
下面有两种方式获取 vlaue
<property name="map">
	<map>
		<entry key="testA" value="aaa"></entry>
		<entry key="testB">
			<value>bbb</value>
        </entry>
	</map>
</property>

3.使用注解注入

看下面

springIoC的注解

前提: 开启注解扫描(掌握)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为
    context名称空间和约束中-->
    <context:component-scan base-package="com.itheima"></context:component-scan>
</beans>

用于创建对象的, 作用在类上

在类上面添加注解 , 并将类放入spring容器

@Component 
作用:  
	把资源让 spring 来管理。相当于在 xml 中配置一个 bean。 
属性:  
	value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。 

下面三个的作用/属性 和 @Componet一样 一般用下面的

@Controller:一般用于表现层的注解。  
@Service:一般用于业务层的注解。  
@Repository:一般用于持久层的注解。 
细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value在赋值是可以不写。 
1.在dao层(持久层)的实现类上添加注解
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao{
    public  void saveAccount(){
        System.out.println("保存了账户1111111111111");
    }
}
2.在service层(业务层)的实现类添加注解
@Service("accountService")
//@Scope("prototype")//表示单例或多例
public class AccountServiceImpl implements IAccountService {
    
}

springDI的注解

用于注入数据的, 作用在方法上

在方法上添加注解注入

@Autowired

作用:  
	自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他 bean 类型。
	当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错.		
	通俗的说: 先根据类型匹配, 再根据名称匹配

代码实现

@Autowired
private IAccountDao accountDao;

@Qualifier

作用:  
	在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,
	必须和 @Autowire 一起使用;但是给方法参数注入时,可以独立使用。 
属性: 
	value:指定 bean 的 id。 

代码实现

@Autowire
@Resource(name = "accountDao")
private IAccountDao accountDao;

@Resource 常用

作用:  
	直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
    	先根据名称匹配, 再根据类型匹配
属性:  
	name: 指定 bean 的 id.
	type: 指定 bean 的 数据类型

代码实现

@Resource(name = "accountDao")
private IAccountDao accountDao = null;

@Value

作用:  
	注入基本数据类型和 String 类型数据的 
属性:  
	value:用于指定值 

代码实现

@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;

作用范围

@Scope(掌握)

作用:
    指定bean的作用范围。
属性:
    value:指定范围的值。 
    取值:
    `singleton` 	单例
    `prototype` 	多例

代码

@Service("accountService")
@Scope("prototype")
public class AccountServiceImpl implements IAccountService {
	
}

生命周期

@Scope

作用:  
	指定 bean 的作用范围。 
属性:  
	value:指定范围的值。      
		取值:
		singleton  	单例
		prototype	多例

@PostConstruct

作用:  用于指定初始化方法。

@PreDestroy

作用:  用于指定销毁方法。

新注解

@Configuration

作用:
	用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用 AnnotationApplicationContext(有@Configuration 注解的类.class)。 
属性:
	value:用于指定配置类的字节码 

@ComponentScan

作用: 
	用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的: <context:component-scan base-package="com.itheima"/>是一样的。 
属性: 
	basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。 

@Bean

作用: 
	该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器.
属性: 
	name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。 

@PropertySource

作用: 
	用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到 properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
属性: 
	value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath.

@Import

作用: 
	用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。
属性: 
	value[]:用于指定其他配置类的字节码。 

代码实现

@Configuration
@ComponentScan(basePackages="com.itheima")
@Import({JdbcConfig.class, CacheConfig.class})			
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration { 
    @Bean(name="runner")
    public QueryRunner createQueryRunner(DataSource ds){
        return new QueryRunner(ds);
    }
    @Bean(name="ds")
    public DataSource createDataSource(){
        ComboPooledDataSource ds = new ComboPooledDataSource();
        //四个连接配置
        ds.setDriverClass(driver);
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy02");
        ds.setUser(username);
        ds.setPassword(password);
        return ds;
    }
}

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

spring 整合 Junit

为了减少对象的创建: 
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    AccountService as = ac.getBean("accountServiceImpl",AccountService.class);
	
	我们只需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了

运用注解的方式进行单元测试

1.导入Spring与Junit整合的依赖包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

2.在单元测试类上添加注解

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {

    private UserService userService ;

    @Test
    public void findAll() {
        List<User> users = userService.findAll();
        System.out.println(users);
    }
}

注意

  1. 如果使用注解配置,  `@ContextConfiguration` 应该指定配置类的字节码
  	@ContextConfiguration(classes = SpringConfig.class)

  2. 如果使用配置文件,`@ContextConfiguration` 应该指定配置文件的位置
  	@ContextConfiguration(locations = "classpath:beans.xml")

spring的AOP

动态代理

特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
    基于接口的动态代理
    基于子类的动态代理

(1)JDK的动态代理 (基于接口)

动态代理:
基于接口的动态代理:
    涉及的类:Proxy	
    提供者:JDK官方
如何创建代理对象:
    使用Proxy类中的newProxyInstance方法
创建代理对象的要求:
    被代理类最少实现一个接口,如果没有则不能使用
    代理对象必须和真实对象实现同一个接口
    代理对象和真实对象都是实现相同的接口, 是平级的, 不能互转.
代码实现
//实现一个接口
Producer producer = new ProducerImpl;

/**
newProxyInstance方法的参数:
    ClassLoader:类加载器
        它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
    Class[]:字节码数组
        它是用于让代理对象和被代理对象有相同方法。固定写法。
    InvocationHandler:用于提供增强的代码
        它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
        此接口的实现类都是谁用谁写。
*/

ClassLoader classLoader = producer.getClass().getClassLoader();//固定写法
Class[] interfaces = producer.getClass().getInterfaces();//固定写法

Producer proxy_producer = (Producer) Proxy.newProxyInstance(classLoader,interfaces, new InvocationHandler() {
    /*
                代理逻辑编写的方法: 代理对象调用的所有方法都会触发该方法执行
                    proxy_lenovo就是代理对象
                    参数:
                        1. proxy:代理对象, 就是proxy_lenovo
                        2. method:代理对象调用的方法, 被封装为的对象
                        3. args:代理对象调用方法时, 传递的实际参数
             */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //需要增强的业务逻辑
		.....
        //使用真实对象调用方法
        Object obj = method.invoke(producer, args);
        return obj;
    }
});

proxy.slave(1000);

(2)cglib的动态代理(了解)

pom中导入坐标依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.1_3</version>
</dependency>
动态代理
基于子类的动态代理:
    涉及的类:Enhancer
    提供者:第三方cglib库
如何创建代理对象:
    使用Enhancer类中的create方法
创建代理对象的要求:
    被代理类不能是最终类(因为代理对象是继承了目标对象)
create方法的参数:
    Class:字节码
        它是用于指定被代理对象的字节码。	
    Callback:用于提供增强的代码
        它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
        此接口的实现类都是谁用谁写。
        我们一般写的都是该接口的子接口实现类:MethodInterceptor
代码实现
final Actor actor = new Actor();      
ClassLoader classLoader = producer.getClass().getClassLoader();//固定写法

Producer proxy_producer = (Producer) Proxy.newProxyInstance(classLoader, new MethodInterceptor() { 
    /** 
    * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何 方法进行增强。   
    * 参数:     
    *  前三个和基于接口的动态代理是一样的。     
    *  MethodProxy:当前执行方法的代理对象。     
    * 返回值:     
    *  当前执行方法的返回值     
    */    
    @Override    
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //需要增强的业务逻辑
		.....
        //使用真实对象调用方法
        Object obj = method.invoke(producer, args);
        return obj;
    }
});

AOP概念及作用

AOP:全称是 Aspect Oriented Programming 即:面向切面编程。 
 
	简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的
基础上,对我们的已有方法进行增强。

作用:  在程序运行期间,不修改源码对已有方法进行增强。 
优势:
	减少重复代码
	提高开发效率
	维护方便

AOP的相关术语

**Pointcut(切入点): 就是被检测到需要增强的方法(通常是service层)

**Advice(通知/增强): 就是具体增强的内容,分为前置通知、后置通知、异常通知、最终通知、环绕通知,具体见下图.

Introduction引介:不多用

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

Weaving织入:是一个动作。执行增强代码的过程,就叫织入。

Proxy代理:代理对象

**Aspect(切面): 切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过方法名中的befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。其实我自己更愿意把它称之为: 所有被增强方法被执行时样子的集合。		切面 = 通知 + 切入

spring框架

基于 XML 的 AOP 配置

1.添加坐标依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

2.创建 spring 的配置文件并导入约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

3.配置 spring 的 ioc

<!-- 配置srping的Ioc,把service对象配置进来-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>

4.配置AOP

<!-- 配置Logger类 -->
<bean id="logger" class="com.itheima.utils.Logger"></bean>

<!--配置AOP-->
<aop:config>
    <!--配置切面 -->
    <aop:aspect id="logAdvice" ref="logger">
    <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
    <aop:before method="printLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:before>
    </aop:aspect>
</aop:config>

AOP配置的细节

配置的标签

aop:config:				配置AOP的主标签
aop:before: 			用于配置前置通知
aop:after-returning: 	用于配置后置通知
aop:after-throwing: 	用于配置异常通知
aop:after: 				用于配置最终通知

aop:pointcut:  
作用:   用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。  
属性:
	expression:用于定义切入点表达式。
	id:用于给切入点表达式提供一个唯一标识
	
此标签写在aop:aspect标签内部只能当前切面使用。
它还可以写在aop:aspect外面,此时就变成了所有切面可用

aop:around:
作用:
	用于配置环绕通知
属性:
	method:指定通知中方法的名称。
	pointcut:定义切入点表达式
	pointcut-ref:指定切入点表达式的引用
说明:
	它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
注意:通常情况下,环绕通知都是独立使用的
切入点表达式的写法:
                关键字:execution(表达式)
切入式表达式: * *..*.*(..)	一般常用的是:	* cn.fang.service.*.*(..)
											返回值	包路径.类名.方法名(参数)

通知的几种类型(环绕通知)

环绕通知

配置文件

<!-- 配置srping的Ioc,把service对象配置进来-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>

    <!-- 配置Logger类 -->
    <bean id="logger" class="com.itheima.utils.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
              此标签写在aop:aspect标签内部只能当前切面使用。
              它还可以写在aop:aspect外面,此时就变成了所有切面可用
          -->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置环绕通知 详细的注释请看Logger类中-->
            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>

代码Logger

/**
spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。
在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。 
*/

public Object aroundPringLog(ProceedingJoinPoint pjp){
    Object rtValue = null;
    try{
        Object[] args = pjp.getArgs();//得到方法执行所需的参数

        System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");

        rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)

        System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");

        return rtValue;
    }catch (Throwable t){
        System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
        throw new RuntimeException(t);
    }finally {
        System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
    }
}

基于注解的AOP

1.创建 spring 的配置文件并导入约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

2.配置spring创建容器时要扫描的包

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

3.在通知类上使用@Aspect 注解声明为切面

@Aspect
作用:
	把当前类声明为切面类

4.在增强的方法上使用注解配置通知

@Before("execution(* com.itheima.service.impl.*.*(..))"): 			前置通知
@AfterReturning("execution(* com.itheima.service.impl.*.*(..))"): 	后置通知
@AfterThrowing("execution(* com.itheima.service.impl.*.*(..))"):	异常通知
@After("execution(* com.itheima.service.impl.*.*(..))"): 			最终通知

5.在 spring 配置文件中开启 spring 对注解 AOP 的支持

<!-- 开启 spring 对注解 AOP 的支持 --> 
    <aop:aspectj-autoproxy/>

spring中的JdbcTemplate

概述

它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装

使用步骤

1.导入坐标

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

2.添加spring配置文件,以及配置数据源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	配置Dao层, 存入spring
	<bean id="accountDao" class="cn.fang.dao.impl.AccountDaoImpl">
        <property name="template" ref="jdbcTemplate"/>
    </bean>
	配置jdbcTemplate, 存入spring, 获取数据源
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
	使用spring的内置数据源
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="DriverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///eesy"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</bean>

3.Dao层的CRUD

public class AccountDaoImpl implements AccountDao {

    private JdbcTemplate template;

    public void setTemplate(JdbcTemplate template) {
        this.template = template;
    }

    //查询所有用户
    @Override
    public List<Account> findAllAccount() {
        List<Account> accounts = template.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
        return accounts.isEmpty() ? null : accounts;
    }

    //查询一个用户
    @Override
    public Account findById(Integer id) {
        List<Account> accounts = template.query("select * from account where id = ? ", new BeanPropertyRowMapper<>(Account.class), id);
        if (accounts.isEmpty()) {
            return null;
        }
        if (accounts.size() > 1) {
            throw new RuntimeException("查询的用户过多");
        }
        return accounts.get(0);

    }

    //保存用户
    @Override
    public void saveAccount(Account account) {
        template.update("insert into account values(null,?,?)", account.getName(), account.getMoney());
    }

    //删除用户
    @Override
    public void deleteAccount(Integer id) {
        template.update("delete from account where id = ?",id);
    }
}

4.测试代码

@Test
public void dao(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    AccountDao ad = ac.getBean("accountDao",AccountDao.class);

    List<Account> accounts = ad.findAllAccount();
    System.out.println(accounts.toString());

}

总结

将Dao层的重复代码进行封装, 创建JdbcTemplate对象
private JdbcTemplate template;

public void setTemplate(JdbcTemplate template) {
    this.template = template;
}

只需要继承JdbcDaoSupport就可以了,用getJdbcTemplate对象执行方法
例如:
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{
    //查询所有用户
    @Override
    public List<Account> findAllAccount() {
        List<Account> accounts = getJdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
        return accounts.isEmpty() ? null : accounts;
    }
}

注意: 可以继承也可以不继承, 继承以后不能用注解

spring中的事务控制

PlatformTransactionManager

此接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法,有三中操作
	获取事物状态信息
	TransactionStatus getTransaction(@Nullable TransactionDefinition var1);
	提交事物
    void commit(TransactionStatus var1) throws TransactionException;
	回滚事物
    void rollback(TransactionStatus var1) throws TransactionException;

真正管理事物的对象

org.springframework.jdbc.datasource.DataSourceTransactionManager	使用Spring JDBC或iBatis进行持久化数据时使用

TransactionDefinition

它是事务的定义信息对象
	事务的隔离级别
		isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
	事务的传播行为
		propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS(没有事务)

TransactionStatus

此接口提供的是事务具体的运行状态

基于 XML配置方式(重点)

1.配置坐标

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

2.创建 spring 的配置事务的步骤 (固定写法…)

spring中基于XML的声明式事务控制配置步骤
    1、配置事务管理器
    2、配置事务的通知
            此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
            使用tx:advice标签配置事务通知
                属性:
                    id:给事务通知起一个唯一标识
                    transaction-manager:给事务通知提供一个事务管理器引用
    3、配置AOP中的通用切入点表达式
    4、建立事务通知和切入点表达式的对应关系
    5、配置事务的属性
           是在事务的通知tx:advice标签的内部
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
 
	<!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
		
        /** 配置事务的属性
       	isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
        propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS(没有事务)。
        read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
        timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
        rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
        no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。*/

     	<tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- 配置aop-->
    <aop:config>
        <!-- 配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
        <!--建立切入点表达式和事务通知的对应关系 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
</bean>

基于注解

spring中基于XML的声明式事务控制配置步骤
    1. 配置事务管理器
    2. 开启spring对注解事务的支持
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    3. 在需要事务支持的地方使用@Transaction注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>

    <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>