Spring学习记录

文章目录

        • 引言
          • EJB(Enterprise Java Bean)
          • spring
          • 设计模式
            • 工厂模式
        • 环境配置
        • spring核心api:ApplicationContext
        • 思考
        • spring框架和日志框架整合
        • 注入(injection)
          • set注入详解
            • 1、String和8种基本数据类型
            • 2、数组类型
            • 3、Set标签赋值
            • 4、list赋值
            • 5、map赋值
            • 6、Properties类型
            • 7、用户自定义类型
            • set注入简化
          • 构造注入
            • 构造方法重载
        • 控制反转 和 依赖注入
          • 反转(转移)控制(IOC Inverse Of Control)
          • 依赖注入(Dependency Injection DI)
        • Spring工厂创建复杂对象
          • Spring工厂创建复杂对象的三种方式
            • 1. FactoryBean接口
            • 2.实例工厂创建复杂对象
            • 3.静态工厂
        • Spring工厂创建对象的次数
        • 对象的生命周期
          • 创建阶段
          • 初始化阶段
          • 销毁阶段
        • 配置文件参数化
          • 配置文件参数的开发步骤
        • 自定义类型转换器
        • 后置处理Bean
        • AOP编程
          • 代理设计模式
          • 静态代理存在的问题
          • 动态代理模式
          • 动态代理细节分析
          • Spring动态代理详解
            • 额外功能详解
          • 切入点详解
            • 切入点表达式
            • 类切入点
            • 包切入点
          • 切入点函数
          • AOP编程的概念
            • AOP开发步骤
            • 切面名词解释
            • AOP底层实现原理
          • 动态代理类的创建
            • JDK的动态代理
            • CGlib动态代理
          • Spring工厂加工原始对象
            • 基于AspectJ注解的AOP编程
            • 切入点复用
        • 持久层整合
          • Spring与Mybatis进行整合
          • SqlSessionFactoryBean
          • Spring 和Mybatis整合的开发步骤
        • Spring的事务处理
          • 如歌控制事务
          • Spring控制事务的开发
        • Spring事务属性
          • 隔离属性
            • 脏读
            • 不可重复读
            • 幻影读
            • 总结
            • 默认隔离属性
          • 传播属性(propagation)
          • 只读属性
          • 超时属性
          • 异常属性
        • Spring整合MVC框架
          • Spring与struts2整合
          • Spring+Struts+Mybatis整合(SSM)
        • 注解编程
          • Spring的基础注解(Spring2.x)
            • 1. 对象创建相关注解
            • 2. 注入相关注解
            • 3. 注解扫描详解
            • 4. 对于注解开发的思考
            • 5. SSM整合开发(半注解开发)
          • Spring的高级注解(Spring3.x及以上)
            • 1. 配置Bean
            • 2. @Bean注解
            • 3.@ComponentScan注解
            • 4.Spring工厂创建对象的多种配置方式
            • 5. 整合多个配置信息
            • 6. 配置Bean的底层实现原理
            • 7. 四维一体的开发思想
            • 8. 纯注解版AOP编程
            • 9.纯注解Spring+Mybatis整合
            • 10. 纯注解版事务编程
            • 11. Spring框架中YML的使用

引言

EJB(Enterprise Java Bean)
  1. 运行环境苛刻

  2. 代码移植性差

总结:EJB是重量级框架

spring

spring是一个轻量级javaEE解决方案,整合了多种优秀的设计模式

  • 轻量级

对于运行环境没有额外的要求(开源tomcat,收费weblogic websphere等),代码移植性高

  • javaEE解决方案

java开发mvc开发中,struts2属于controller,mybatis属于dao,而spring可以解决在javaEE的整个开发过程中每一层的问题(springmvc->controller,aop->service…)

  • 整合设计模式
  1. 工厂
  2. 代理
  3. 模板
  4. 策略
设计模式

面向对象设计中,解决特定问题的经典代码

工厂模式

耦合:代码间的强关联关系,一方的改变会影响到另一方,不利于代码的维护(理解:接口的实现类硬编码到程序中)
概念:通过工厂类创建对象
好处:解耦合

//对象的创建方式:
// 1.直接调用构造方法创建对象  
UserService userService = new UserServiceImpl();
// 2.通过反射的形式创建对象解耦合
Class clazz = Class.forName("***.UserServiceImpl");
UserService userService = (UserService)clazz.newInstance();

通用工厂模式

public class BeanFactory {
     
	private static Properties env = new Properties();
	static {
     
		InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
		try {
     
			env.load(inputStream);
		} catch (IOException e) {
     
			e.printStackTrace();
		}
	}

	public static Object getBean(String key) {
     
		Object ret = null;
		try {
     
			Class clazz = Class.forName(env.getProperty(key));
			ret = clazz.newInstance();
		} catch (Exception e) {
     
			e.printStackTrace();
		}
		return ret;
	}
}

spring本质:工厂 ApplicationContext

环境配置

  • Spring的jar包

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.1.4.RELEASEversion>
dependency>
<dependency>
     <groupId>org.springframeworkgroupId>
     <artifactId>spring-webmvcartifactId>
     <version>5.1.4.RELEASEversion>
dependency>
  • 单元测试

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.13version>
    <scope>testscope>
dependency>

spring核心api:ApplicationContext

作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
好处:解耦合

ApplicationContext接口类型

接口:屏蔽实现的差异
非web环境:ClassPathXmlApplicationContext(main,junit)
web环境:XmlWebApplicationContext

ApplicationContext是一个重量级资源

  • ApplicationContext工厂对象占用内存比较大,不会频繁的创建对象,一个应用只会创建一个工厂对象。

  • ApplicationContext工厂一定是一个线程安全的(支持多线程并发访问)

Spring工厂创建的对象,叫做bean或者Component

package xyz.hongxiao2020;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.junit.Assert.*;

public class PersonTest {
     
    @Test
    public void test1(){
     
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        Person person = (Person) applicationContext.getBean("person");
        System.out.println("person = " + person);
    }

    @Test
    public void test() {
     
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        //不需要强转
        Person person = ctx.getBean("person", Person.class);
        System.out.println("person = " + person);

        //当前Spring的配置文件中只能有一个bean的class是Person类型
        Person person = ctx.getBean(Person.class);
        System.out.println("person = " + person);

        //获取的是Spring工厂配置文件中所有bean标签的id值
        String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
     
            System.out.println("beanDefinitionName = " + beanDefinitionName);
        }

        //根据类型获得Spring配置文件中id值
        String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
        for (String id : beanNamesForType) {
     
            System.out.println("id = " + id);
        }

        //用于判断是否存在指定id值得bean,不能判断name值
        if (ctx.containsBeanDefinition("person")) {
     
            System.out.println("存在person");
        }else{
     
            System.out.println("不存在person");
        }

        //用于判断是否存在指定id值得bean,也可以判断name值
        if (ctx.containsBean("person")) {
     
            System.out.println("存在" );
        }else{
     
            System.out.println("不存在");
        }
    }
}
  1. 只配置class属性
<bean class="xyz.hongxiao2020.Person"/>
a) 上述这种配置默认的id值为xyz.hongxiao2020.Person#0
b) 应⽤场景: 如果这个bean只需要使用一次,那么就可以省略id值
            如果这个bean会使用多次,或者被其他bean引用则需要设置id值
  1. 配置name属性

作用:用于在Spring的配置文件中,为bean对象定义别名(小名)

1. ctx.getBean("id|name") -->object
2. 

反射的底层调用了对象自己的无参构造方法,即使构造方法是私有的,Spring工厂依然可以调用其构造方法。

思考

问题:在未来的开发过程中,是不是所有的对象都交给Spring工厂来创建那

回答:理论上是的。但是有特例:实体对象(entity)是不会交给spring创建的,它是由持久层框架进行创建的。

spring框架和日志框架整合

Spring与日志框架整合,日志框架就可以在控制台中输出Spring框架运行过程中的一些重要信息

好处:便于了解Spring框架的运行过程,便于调试。

默认:
Spring1.2.3早期都是与commons-logging.jar
Spring5.x默认整合的日志框架:logback log4j2

Spring5.x整合log4j

  1. 引入log4j jar包
  2. 引入log4.properties配置文件
<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-log4j12artifactId>
    <version>1.7.25version>
dependency>
<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>

resources 文件夹根目录下(log4j.properties)

### 配置根
log4j.rootLogger = debug,console
### 日志输出到控制台显示
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

注入(injection)

通过Spring工厂及配置文件,为成员变量赋值

注入好处:解耦合

set注入详解

前提是先为成员变量设置set函数,底层是通过set方法来完成属性赋值
针对不同类型的成员变量,在标签中,需要嵌套其他标签

<bean id="person" class="xyz.hongxiao2020.Person">
    <property name="age" value="10">property>
    <property name="name" value="hongxiao">property>
bean>
1、String和8种基本数据类型

直接使用Value赋值

2、数组类型
<property name="emails">
    <list>
        <value>wwwvalue>
        <value>cccvalue>
        <value>bbbvalue>
    list>
property>
3、Set标签赋值
<set>
    <value>111value>
    <value>222value>
    <value>333value>
set>
4、list赋值
<list>
    <value>wwwvalue>
    <value>cccvalue>
    <value>bbbvalue>
list>
5、map赋值
注意: map -- entry -- key有特定的标签 <key>key>
                         值根据对应类型选择对应类型的标签value或者ref
<map>
    <entry>
        <key><value>hellovalue>key>
        <value>123value>
    entry>
    ...
map>
6、Properties类型

Properties是一种特殊的Map,他的key是String Value也是String类型

<props>
    <prop key="k1">value1prop>
    <prop key="k2">value2prop>
props>
7、用户自定义类型

第一种方式:

  • 为成员变量提供set get方法
  • 配置文件中进行注入(赋值)
<bean id="userService" class="xxxx.UserServiceImpl">
<property name="userDAO">
	<bean class="xxx.UserDAOImpl"/>
property>
bean>

第二种方式:
第一种赋值方式存在的问题:

  • 配置文件代码冗余
  • 被注入的对象userDao,多次创建,浪费JVM内存资源
<bean id="userDAO" class="xxx.UserDAOImpl"/>
<bean id="userService" class="xxx.UserServiceImpl">
	<property name="userDAO">
		<ref bean="userDAO"/>
	property>
bean>
set注入简化
<property name="name">
	<value>testvalue>
property>

<property name="name" value="test"/>


<property name="userDAO">
	<ref bean="userDAO"/>
property>
property>

利用p标签简化

<bean id="person" class="*.Person" p:name="hong" p:age="23">
构造注入

通过Spring的配置文件,为成员变量赋值

Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值

构造注入:Spring调用构造方法,通过配置文件,为成员变量赋值

  • 为对象提供有参构造方法

  • spring配置文件设置

  • <constructor-arg>
        <value>hellovalue>
    constructor-arg>
    <constructor-arg>
        <value>23value>
    constructor-arg>
    
构造方法重载

参数不同时:根据控制的数量进行区分

参数相同时:通过在标签引入Type属性进行区分

未来的实战中,应用set注入还是构造注入?
:set注入更多

  1. 构造注入麻烦 (重载)
  2. Spring框架底层 大量应用了set注入

控制反转 和 依赖注入

反转(转移)控制(IOC Inverse Of Control)

控制:对于成员变量赋值的控制权

控制反转:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中完成

优点:解耦合

底层实现:工厂模式

依赖注入(Dependency Injection DI)

注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值

依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)

Spring工厂创建复杂对象

复杂对象

简单对象:指的是可以直接通过new(构造方法)创建对象

复杂对象:指的是不可以直接通过new构造方法创建的对象(Connection SqlSessionFactory)

Spring工厂创建复杂对象的三种方式
1. FactoryBean接口

开发步骤:

  • 实现FactoryBean接口
  • Spring配置文件的配置
public class ConnectionFactoryBean implements FactoryBean<Connection> {
     
    //用于书写创建复杂方法的代码,并把复杂对象作为方法的返回值返回
    public Connection getObject() throws Exception {
     
       Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bigData?useSSL=false","root","root");
        return conn;
    }
    //返回所创建复杂对象的Class对象
    public Class<?> getObjectType() {
     
        return Connection.class;
    }
    //是否是单例模式
    public boolean isSingleton() {
     
        return false;
    }
}

spring配置文件的配置


<bean id="conn" class="*.MyFactoryBean">bean>

细节:

  • 如果就想要获得FactoryBean对象怎么办:ctx.getBean("&conn")获得的就是FactoryBean对象
  • isSingleton方法
    • 返回true只会创建一个复杂对象
    • 返回false每次都会创建一个对象
    • 根据对象的特点返回true(sqlSessionFactory)或者false(Connection)

通过注入的方式,解耦

<bean id="conn" class="*.MyFactoryBean">
    <property name="className" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/bigData?useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
bean>

接口回调

  1. 为什么Spring规定FactoryBean接口实现 并且创建复杂对象的方法写在 getObject()?
  2. ctx.getBean(“conn”) 获得是复杂对象 Connection,而没有获得ConnectionFactoryBean("&conn")

Spring内部运行流程

  1. 通过conn获得ConnectionFactoryBean类的对象 ,进而通过instanceof(FactoryBean) 判断出是FactoryBean接口的实现类

  2. Spring按照规定 getObject() --->Connection

  3. 返回Connection

总结:
FactoryBean是Spring用于创建复杂类型对象的一种方式,也是Spring原生提供的,Spring在整合其他框架时会大量应用FactoryBean

2.实例工厂创建复杂对象
  • 避免Spring框架的侵入
  • 整合遗留系统
public class FactoryBean {
     
    public Connection getConnection(){
     
        Connection connection = null;
        try {
     
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql", "root", "root");
        } catch (ClassNotFoundException e) {
     
            e.printStackTrace();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }
        return connection;
    }
}
<bean id="conn" class="*.FactoryBean">bean>
<bean id="conn2" factory-bean="conn" factory-method="getConnection">bean>
3.静态工厂
<bean id="conn2" class="*.StaticFactoryBean" factory-method="getConnection">bean>

Spring工厂创建对象的次数

spring工厂控制简单对象创建次数

默认为单例模式

<bean id="person" class="*.Person" scope="prototype/singleton">bean>

为什么要控制对象的创建次数

最大的好处:节省不必要的内存浪费

什么样的对象只创建一次:sqlSessionFactory Dao Service (共用,线程安全)

什么样的对象需要多次创建:Connection SqlSession | Session

对象的生命周期

什么是生命周期:一个对象创建、存活、消亡的一个完整过程

为什么要学习对象的生命周期:

由Spring负责对象的创建、存活、销毁,了解生命周期,有利于使用好Spring为我们创建对象
创建阶段

Spring工厂何时创建对象:

scope = singleton  Spring在工厂创建的同时,会创建对象
scope = prototype Spring工厂会在获取对象的同时创建对象 (ctx.getBean)

如果想在scope = singleton 模式下设置获取对象的时候创建对象需要设置 lazy-init="true"

初始化阶段

Spring工厂会在创建完对象后,调用对象的初始化方法,完成对象相应的初始化操作

  • 初始化方法提供:程序员根据需求,提供初始化方法,最终完成初始化操作
  • 初始化方法调用:Spring工厂进行调用

方式一:实现InitializingBean 接口实现它的方法

public void afterPropertiesSet() throws Exception {
     

}

方式二:对象中提供一个普通方法

public void init(){

}

<bean id="prod" class="*.Product" init-method="init"/>
如果一个对象即实现InitializingBean 同时又提供普通的初始化方法顺序
1.  InitializingBean
2.  普通初始方法

注入一定发生在初始化之前

什么叫初始化?

资料的初始化:数据库, IO, 网络  ......
销毁阶段

Spring销毁对象前,会调用对象的销毁方法,完成销毁操作

  • spring什么时候销毁所创建的对象:ctx.close()
  • 销毁方法:程序员根据自己的需求,定义销毁方法,完成销毁操作
  • 调用:Spring工厂调用

方式一:实现DisposableBean

方式二:定义一个方法,在配置文件中配置destroy-method=""

细节分析:**销毁方法只适用于scope为singleton模式下**

什么叫销毁操作:主要指的是资源的释放操作 io.close()  connetion.close()

配置文件参数化

什么是配置文件参数化:把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中,有利于Spring配置文件的维护

1、Spring配置文件中存在需要经常修改的字符串?

存在,以数据库连接相关的参数为代表

2、经常变化的字符串,在Spring配置文件中,直接修改有什么影响

不利于项目的维护

3、转移到小的配置文件中(.properties)

利于维护
配置文件参数的开发步骤
名字:随便.properties
放置位置:随便(java文件夹与resource文件夹编译后是放在一起的)
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/bigData?useSSL=false
jdbc.username = root
jdbc.password = root
spring配置文件与小配置文件的整合
<context:property-placeholder location="classpath:/mysql.properties"/>
<bean id="conn" class="xyz.hongxiao2020.ConnectionFactoryBean">
	<property name="driverClassName" value="${jdbc.driverClassName}"/>
	<property name="url" value="${jdbc.url}"/>
	<property name="username" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
bean>

自定义类型转换器

作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成对象成员变量对应的数据类型,进而完成了注入

原因:当Spring内部没有提供特定的类型转换器的时候,而程序员在应用的过程中还需要使用,那么程序员就需要自定义类型转换器

如何自定义一个类型转换器:

  1. 实现convert接口
//第一个参数是需要被转换的类型,第二个参数是目标参数
public class MyDateConverter implements Converter<String, Date> 

在Spring中获取日期字符串:

//直接就可以获取日期字符串
public Date convert(String s) {
     
		Date parse = null;
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		try {
     
			parse = format.parse(s);
		} catch (ParseException e) {
     
			e.printStackTrace();
		}
		return parse;
}
  1. Spring配置文件进行注册

<bean id="convert" class="xyz.hongxiao2020.MyDateConverter"/>

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
	<property name="converters">
		<set>
			<ref bean="convert"/>
		set>
	property>
bean>

一些细节:

  • 日期格式可以在配置文件中定义,减小程序之间的耦合性
<bean id="convert" class="xyz.hongxiao2020.MyDateConverter">
	<property name="pattern" value="yyyy-MM-dd"/>
bean>
  • ConversionServiceFactoryBean对象的id必须是conversionService

  • spring提供的默认日期转换器转换的格式为2020/07/22

后置处理Bean

BeanPostProcessor作用:对Spring工厂所创建的对象再加工。

//程序员需要实现BeanPostProcessor规定接口中的方法
public Object postProcessBeforeInitialization(Object bean, String beanName)
//作用:Spring创建完对象并进行注入,之后运行Before方法进行加工
//获得Spring创建好的对象:通过方法的参数最终通过返回值交给Spring框架

public Object postProcessAfterInitialization(Object bean, String beanName)
//作用:Spring执行完对象初始化操作后,可以运行After方法进行加工
//获得Spring创建好的对象 :通过方法的参数
//最终通过返回值交给Spring框架

//实战中:很少处理Spring的初始化方法:没有必要区分Before和After方法。只需要执行其中一个After方法即可
	
//注意:使用Before方法需要返回bean对象
//		postProcessBeforeInitiallization
//		return bean;

两个步骤: 实现BeanPostProcessor接口

public class MyBeanPostProcessor implements BeanPostProcessor {
     
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
     
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
     
        if (bean instanceof Person){
     
            Person person = (Person) bean;
            person.setName("after postProcess...");
        }
        return bean;
    }
}

在Spring配置文件中配置

<bean id="beanPostProcessor" class="xyz.hongxiao2020.MyBeanPostProcessor">bean>

BeanPostProcessor会对Spring工厂中创建的所有对象进行加工,所以我们要判断出需要加工的对象(instance of)

AOP编程

AOP使用了23种设计模式中的静态代理设计模式

为什么要使用代理设计模式?

在JavaEE分层开发中,哪个层次对我们最重要

DAO-Service-Controller  答案是Service 因为里面封装了我们想要实现的业务,用于满足用户需求

Service包含了哪些代码:

Service层中 = 核心功能(几十行 上百行代码)+ 额外功能(附加功能)

1. 核心功能
	1. 业务运算 
	2. Dao调用
2. 额外功能
	1. 不属于业务
	2. 可有可无
	3. 代码量很小
	比如(事务,日志,性能)

问题:额外功能写在Service层好不好?

  • Service层的调用者角度(Controller):需要在Service层书写额外功能(需要)
  • 软件设计者:Service不需要额外功能
代理设计模式

概念:通过代理类,为原始类(目标)增加额外的功能

好处:利于原始类(目标)的维护

代理开发的核心要素:代理类 = 目标类 + 额外功能 + 原始类实现相同的接口

//实现相同的接口
public class UserServiceProxy implements UserService {
     
	//创建原始对象
    private UserServiceImpl userService = new UserServiceImpl();
    public void register(User user) {
     
        //实现额外功能
        System.out.println("---------额外功能----------");
        userService.register(user);

    }

    public boolean login(String name, String password) {
     
        System.out.println("---------额外功能----------");
        return userService.login(name,password);
    }
}

静态代理:每一个原始类都会手工编写一个代理类

静态代理存在的问题
  • 静态代理类文件数目过多,不利于项目管理

  • 额外功能代码维护性差

动态代理模式

动态代理相关jar包

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-aopartifactId>
    <version>5.1.14.RELEASEversion>
dependency>

<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjrtartifactId>
    <version>1.8.8version>
dependency>

<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.8.3version>
dependency>

动态代理开发步骤:

  1. 创建原始对象(并在配置文件中设置)

  2. 额外功能

- MethodBeforeAdvance接口

- 额外的功能写在该接口的实现中,在原始方法执行前运行额外功能
//把运行原始方法执行前的运行的额外功能,书写在before方法中
public class Before implements MethodBeforeAdvice {
     
    public void before(Method method, Object[] objects, Object o) throws Throwable {
     
        System.out.println("MethodBeforeAdvice ......");
    }
}
<bean id="before" class="xyz.hongxiao2020.Before"/>
  1. 定义切入点

切入点:额外功能加入的位置

目的:程序员根据自己的需求,决定额外功能加入给哪个原始方法

<aop:config>
    
    <aop:pointcut id="pc" expression="execution(* *(..))"/>
aop:config>
  1. 组装(2,3整合)
<aop:config>
	<aop:pointcut id="pc" expression="execution(* *(..))"/>
	<aop:advisor advice-ref="before" pointcut-ref="pc"/>
aop:config>
  1. 调用

目的:获得Spring工厂创建的动态代理对象,并进行调用

注意:

Spring的工厂通过原始对象的id值获取的对象为代理对象
获得代理对象后,可以通过声明接口类型,进行对象的存储UserService userService = (UserService) ctx.getBean("userService");

动态代理细节分析

Spring创建的代理类在哪那?

Spring框架在运行时,通过动态字节码技术,在JVM中创建的,在JVM内部,等程序结束后,会和虚拟机一起消失

什么是动态字节码技术:通过第三方动态字节码框架,在虚拟机中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失

结论: 动态代理不需要创建类文件,都是Jvm在运行过程中创建的(没有.java .class文件在本地生产),所以不会造成静态代理类文件数量过多,影响项目管理问题

动态代理模式简化代理的开发:

  • 在额外功能不改变的前提下,创建其他目标类(原始类)的代理对象时,只需要指定原始(目标)对象即可。

动态代理额外功能的维护性大大增强

打开扩展,关闭修改

Spring动态代理详解
额外功能详解
//Method:额外功能所增加给的那个原始方法
//Object[]:额外功能所增加给的那个原始方法的参数
//Object:额外功能所增加给的那个原始对象
public class Before implements MethodBeforeAdvice {
     
    public void before(Method method, Object[] objects, Object o) throws Throwable {
     
        System.out.println("MethodBeforeAdvice ......");
    }
}

MethodInterceptor(方法拦截器)

MethodBefore:原始方法执行之前

MethodInterceptor:在方法执行之前和之后都可以使用(实战中使用更多)

/*
invoke方法的作用:额外功能书写在invoke
					额外功能  运行情况(3种):1.原始方法之前
							 			   2.原始方法之后
							               3.原始方法之前和之后(事务)
							               4.抛出异常时
确定:原始方法怎么运行
参数:MethodInvocation:额外功能所增加给的那个原始方法
调用原始方法:invocation.proceed() 
返回值:Object: 代表原始方法的返回值
额外功能运行在原始方法执行之前
*/
public class Arround implements MethodInterceptor {
     
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
     
        System.out.println("-----额外功能 log----");//额外功能
        Object ret = methodInvocation.proceed();
        return ret;
    }
}

总结:invocation.proceed() 就是原始方法,之前和之后自己可以添加内容

原始方法会抛出异常时:

public Object invoke(MethodInvocation invocation) throws Throwable {
     
	Object ret = null;
	try {
     
	ret = invocation.proceed();
	} catch (Throwable throwable){
     
	System.out.println("-----原始方法抛出异常 执⾏的额外功能 ----");
	throwable.printStackTrace();
	}
	return ret;
}
切入点详解
切入点决定额外功能的加入位置(方法)

execution() 切入点函数
* *(..) 切入点表达式
切入点表达式
//修饰符 返回值 方法名 参数表
 public void   add  (int a, int b)
execution(* *(..))
* --> 修饰符  返回值
* --> 方法名  
( ) --> 参数表
..  --> 对于参数没有要求  

定义login方法为切入点

execution(* login(..))
只在login方法上切入
# 定义login方法且login方法有两个字符串类型的参数作为切入点
* execution(* login(String, String))
# 注意 对于一些引用变量,非java.lang包中的类型,需要加入全限定类名
* register(xyz.hongxiao2020.demo.User)
# ..可以和具体的参数连用
* login(String, ..)

上面所写的切入点表达式最大的缺点是匹配不精准

精准方法切入点限定:包.类.方法(参数)

*表示包名和类名
<aop:pointcut id="pc" expression="execution(* xyz.hongxiao2020.demo.UserServiceImpl.login(..))"/>
类切入点

指定类作为额外功能加入的位置

语法一:

<aop:pointcut id="pc" expression="execution(* *.UserServiceImpl.*(..))"/>

语法二:

# 忽略包
1. 类只存在一级包
* *.UserServiceImple.*(..)
2. 类存在多级包(包含一级包)
* *..UserServiceImpl.*(..)
包切入点
# 指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能
# 注意:	切入点包中所有的类,必须在demo包中,不能在demo包的子包中
* xyz.hongxiao2020.demo.*.*(..)

# 当前包及其子包
* xyz.hongxiao2020.demo..*.*(..)

在实战中更为应用广泛的是包切入点

切入点函数

作用:用于执行切入点表达式

  1. execution
# 最为重要的切入点函数,功能最全
* 执行方法切入点表达式,类切入点表达式,包切入点表达式

弊端:execution 执行切入点表达式,书写麻烦

# 注意:其他切入点函数 是简化execution书写复杂度,功能上完全一致
  1. args
# 作用:主要用于函数(方法参数)的匹配
例如:切入点为方法参数必须是两个字符串类型的参数
execution(* *(String, String))
args(String,String)
  1. within
# 作用:主要用于包、类切入点表达式的匹配
例如:切入点为UserServiceImpl这个类
execution(* *..UserServiceImpl.*(..))
within(*..UserServiceImpl)

execution(* xyz.hongxiao2020.demo..*.*(..))
within(xyz.hongxiao2020.demo..*)
# 其实也就是把execution中的修饰符和方法名简化了,只关注包
  1. @Annotation

作用:为具有特殊注解的方法加入额外功能

//注解所在位置
@Target(ElementType.METHOD)
//注解在什么时候执行 一般都是Runtime
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
     
}
<aop:pointcut id="pc" expression="@annotation(xyz.hongxiao2020.demo.Log)"/>
  1. 切入点函数的逻辑运算

指的是整合多个切入点函数一起配合工作,进而完成更为复杂的需求

  • and操作
案例:login同时参数为两个字符串
execution(* login(String, String))
execution(* login(..)) and args(String, String)
# 注意 与操作不能用于同种类型的切入点函数
  • or操作
案例:register方法和login方法作为切入点
execution(* login(..)) or execution(* register(..))
AOP编程的概念
AOP(Aspect Oriented Programing)面向切面编程  =  spring动态代理开发
以切面为基本单位进行程序开发,通过切面间的彼此调用,相互协同,完成程序的构建
切面 = 切入点 + 额外功能
OOP(Object Oriented Programing)面向对象编程
以对象为基本单位进行程序开发,通过对象间的彼此协同,相互协调,完成程序的构建
POP(Producer Oriented Programing)面向过程(方法,函数)编程
以过程为基本单位的程序开发,通过彼此间协同,相互调用,完成程序的构建

AOP:本质上就是 Spring动态代理开发,有利于原始类的维护

# AOP不可能取代OOP,它只是OOP的有意补充
AOP开发步骤
1. 原始对象
2. 额外功能(MethodInterceptor)
3. 切入点
4. 组装切面(额外功能 + 切入点)
切面名词解释

切面= 切入点 + 额外功能

几何学:面 = 点 + 相同的功能

AOP底层实现原理
  1. 核心问题

AOP如何创建动态代理类(动态字节码技术)

  1. Spring是如何通过原始对象的ID值获取代理对象?
动态代理类的创建
JDK的动态代理

代理创建三要素:1 原始对象 2 额外功能 3 代理对象实现相同的接口

public static void main(String[] args) {
     
    //创建原始对象
    UserService userService = new UserServiceImpl();
    //JDK创建动态代理
    Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
}
public interface InvocationHandler {
      //演示原理,没有设置实现类
    //作用: 用于书写额外功能 额外功能运行原始方法执行前后 抛出异常
    // 参数:proxy 忽略掉,代表的是代理对象
    //  	method 额外功能所增加给的那个原始方法
    //		Object[] args 原始方法的参数
    public Object invoke(Object proxy, Method method, Object[] args){
     
        Object ret = method.invoke(userService,args);
        return ret;
    }
}
# interfaces:原始对象所实现的接口
userService.getClass().getInterfaces()

类加载器的作用:

  • 通过类加载器把对应类的字节码加载到JVM中
  • 通过类加载器创建class对象,进而创建这个类的对象

如何获得类加载器:每个类的.class文件 自动分配与之对应的ClassLoder

在动态代理创建的过程中,需要ClassLoader创建代理类的Class对象,可是动态代理类没有对应的.class文件,JVM也不会为其分配ClassLoader,但是又需要怎么办?(借用一个ClossLoader)

# ClassLoader:完成代理类的创建
//创建代理类的class对象,进而完成代理类的创建  
//注意:类加载器是借用来的(TestJDKProxy或者UserService都行)
public class TestJDKProxy {
     
    public static void main(String[] args) {
     
        final UserService userService = new UserServiceImpl();
        InvocationHandler handler = new InvocationHandler() {
     
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
                System.out.println("------proxy log------");
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
        userServiceProxy.login("pzh", "1234");
        userServiceProxy.register(new User("pzh", "1234"));
    }
}

CGlib动态代理
// 对于一些没有实现接口的类
public class UserService {
     
    private UserDao userDao= new UserDaoImpl();
    public void register(User user) {
     
        userDao.save(user);
    }

    public void login(String name, String password) {
     
        userDao.queryGet(name,password);
    }
}

//代理类 继承你要代理的类
public clss Proxy extends UserService(){
     
	login(){
     
	 	//额外功能
	 	super.login();
	}
}
public class ProxyCglib {
     
    public static void main(String[] args) {
     
        final UserService userService = new UserService();
         /*
        通过cglib方式创建动态代理对象
        Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
        cglib同样也需要做这些:
        enhancer.setClassLoader();
        enhancer.setSuperclass();
        enhancer.setCallback(); -->MethodInterceptor(cglib包下)
     */
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(ProxyCglib.class.getClassLoader());
        enhancer.setSuperclass(UserService.class);
        MethodInterceptor interceptor = new MethodInterceptor() {
     
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
     
                System.out.println("-----cglib log-------");
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };
        enhancer.setCallback(interceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("pzh","1234");
        userServiceProxy.register(new User("pzh","1234"));
    }
}

面试问AOP要把这两个动态代理回答出来

  • 总结
    • JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理类
    • Cglib动态代理 Enhancer 通过继承父类创建的代理类
Spring工厂加工原始对象

模拟

<bean id="userService" class="xyz.hongxiao2020.factory.UserServiceImpl"/>
<bean id="proxyBeanPostProcessor" class="xyz.hongxiao2020.factory.ProxyBeanPostProcessor"/>
public class ProxyBeanPostProcessor implements BeanPostProcessor {
     
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
     
        return bean;
    }

    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
     
        InvocationHandler handler = new InvocationHandler() {
     
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
                System.out.println("new----log");
                Object ret = method.invoke(bean, args);
                return ret;
            }
        };
        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), handler);
    }
}
基于AspectJ注解的AOP编程
<bean id="userService" class="xyz.hongxiao2020.aspect.UserServiceImpl"/>

<bean id="around" class="xyz.hongxiao2020.aspect.MyAspect"/>

<aop:aspectj-autoproxy/>
//告知程序这是一个切面
@Aspect
public class MyAspect {
     
    @Around("execution(* login(..))")
    public Object around(ProceedingJoinPoint joinPoint){
     
        Object ret = null;
        try {
     
            System.out.println("----aspect log----");
            ret = joinPoint.proceed();
        } catch (Throwable throwable) {
     
            throwable.printStackTrace();
        }
        return ret;
    }
}
切入点复用

在切面类中定义一个是函数 上面 @Pointcut()注解 通过这种方式定义切入点表达式,利于复用

@Aspect
public class MyAspect {
     
    @Pointcut("execution(* login(..))")
    public void myPointcut(){
     

    }
    @Around(value = "myPointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
     
        Object ret = null;
        try {
     
            System.out.println("----aspect log----");
            ret = joinPoint.proceed();
        } catch (Throwable throwable) {
     
            throwable.printStackTrace();
        }
        return ret;
    }

    @Around(value = "myPointcut()")
    public Object around1(ProceedingJoinPoint joinPoint){
     
        Object ret = null;
        try {
     
            System.out.println("----aspect tx----");
            ret = joinPoint.proceed();
        } catch (Throwable throwable) {
     
            throwable.printStackTrace();
        }
        return ret;
    }
}
@Pointcut("execution(* *(..))")
public void pointcut(){
     }

@Around(value = "pointcut()")

默认情况下 AOP编程底层是基于JDK动态代理的开发方式

注解切换方式

//将proxy-target-class设为true就是基于Cglib的开发模式
<aop:aspectj-autoproxy proxy-target-class="true"/>

传统开发方式切换

<aop:config  proxy-target-class="true">
	<aop:pointcut id="pc" expression="execution(* *(..))"/>
	<aop:advisor advice-ref="before" pointcut-ref="pc"/>
aop:config>

如何在原始类中调用代理类的方法

在同一个业务类中,进行业务间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通方法调用,都调用的是原始方法)。如果想让内层的方法也调用代理对象的方法,就要ApplicationContextAware获得工厂,进而获得代理对象

public void register(User user) {
     
    System.out.println("UserService.register");
    //调用的是原始对象的login方法 ---> 核心功能 并不能满足需求
    this.login("hello","123");
}
public class UserServiceImpl implements UserService, ApplicationContextAware {
     

    ApplicationContext ctx = null;
    public void login(String username, String password) {
     
        System.out.println("UserService.login");
    }

/*
            设计目的:代理对象的login方法 --->  额外功能+核心功能
            ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
            UserService userService = (UserService) ctx.getBean("userService");
            userService.login();

            Spring工厂重量级资源 一个应用中 应该只创建一个工厂
         */
    public void register(User user) {
     
        System.out.println("UserService.register");
        //调用的是原始对象的login方法 ---> 核心功能
        UserService userService = (UserService) ctx.getBean("userService");
        userService.login("hello","123");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
     
        ctx = applicationContext;
    }
}

AOP知识总结

AOP编程概念(Spring动态代理开发)

概念:通过代理类为原始类增加额外功能

好处:利于原始类的维护

持久层整合

Spring为什么要与持久层进行整合

  • JavaEE开发中需要持久层对数据库进行访问
  • JDBC Hibernate Mybatis进行持久开发过程存在大量的代码冗余
  • Spring基于模板的设计模式对于上述持久层进行了封装

Spring可以和哪些持久层进行整合

  1. JDBC
  	|- JDBCTemplate
  2. Hibernate(JPA)
  	|- HibernateTemplate
  3. Mybatis
  	|- SqlSessionFactory MapperScannerConfigure
Spring与Mybatis进行整合

Mybatis开发步骤

  1. 实体

    public class User implements Serializable {
           
        private Integer id;
        private String name;
        private String password;
        ......
    
  2. 实体别名

     <typeAliases>
            <typeAlias alias="user" type="xyz.hongxiao2020.mybatis.User"/>
     typeAliases>
    

    完整配置文件为:

    
    
    <configuration>
        <typeAliases>
            <typeAlias alias="user" type="xyz.hongxiao2020.mybatis.User"/>
        typeAliases>
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost/pzh?useSSL=false"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                dataSource>
            environment>
        environments>
        <mappers>
            <mapper resource="UserMapper.xml"/>
        mappers>
    configuration>
    
  3. create table t_users(id int primary key auto_increment,name varchar(12),password varchar(12));
    
  4. 创建DAO接口

    public interface UserDao {
           
        public void save(User user);
    }
    
  5. 实现Mapper文件

    
    
    <mapper namespace="xyz.hongxiao2020.mybatis.UserDao">
        <insert id="save" parameterType="user">
            insert into t_users(name,password) values (#{name},#{password})
        insert>
    mapper>
    
  6. 注册Mapper文件

    <mappers>
    	<mapper resource="UserMapper.xml"/>
    mappers>
    
  7. Mybatis API调用

    public class TestMybatis {
           
        public static void main(String[] args) throws IOException {
           
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession session = sessionFactory.openSession();
            UserDao userDao = session.getMapper(UserDao.class);
            User user = new User();
            user.setName("pzh");
            user.setPassword("1234");
            userDao.save(user);
            session.commit();
        }
    }
    
    

Mybatis在开发中存在的问题:配置繁琐,代码冗余

- 实体 
# 实体别名  配置繁琐
- 表
- 创建DAO接口
- 实现Mapper文件
# 注册Mapper文件 配置繁琐
# Mybatis API调用 代码冗余
SqlSessionFactoryBean
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
# 在Spring中SQLSessionFactoryBean封装了创建SqlSessionFactory的代码
只需要在配置文件中注入SqlSessionFactoryBean对象,就可以省略mybatis-config.xml代码

    
     指定所对应的包名,Spring就会自动创建 User Product
    
    通配的设置 *Mapper.xml --> UserDaoMapper.xml ProductDaoMaper.xml
bean>

在Mybatis-config.xml文件中需要做三件事

  • dataSource
  • TypeAliases
  • Mapper文件注册
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//这里通过配置MapperScannerConfig来设置

    
    设置Dao接口所在的包
 
最终:创建Dao对象
    注意:MapperScannerConfig所创建的DAO对象,他的Id值是接口,单词首字母小写
    UserDAO--> userDAO  ProductDAO --> ctx.getBean("id")
Spring 和Mybatis整合的开发步骤
  • 引入jar包
<dependency>
	<groupId>org.springframeworkgroupId>
	<artifactId>spring-jdbcartifactId>
	<version>5.1.4.RELEASEversion>
dependency>
<dependency>
	<groupId>org.mybatisgroupId>
	<artifactId>mybatis-springartifactId>
	<version>2.0.2version>
dependency>
<dependency>
	<groupId>com.alibabagroupId>
	<artifactId>druidartifactId>
	<version>1.1.18version>
dependency>
<dependency>
	<groupId>mysqlgroupId>
	<artifactId>mysql-connector-javaartifactId>
	<version>5.1.48version>
dependency>
<dependency>
	<groupId>org.mybatisgroupId>
	<artifactId>mybatisartifactId>
	<version>3.4.6version>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.1.4.RELEASEversion>
dependency>
  • 配置文件的(applicationContext.xml)

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/pzh?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    bean>


    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="xyz.hongxiao2020.entity"/>
        <property name="mapperLocations">
            <list>
                <value>classpath:xyz.hongxiao2020.mapper/*Mapper.xmlvalue>
            list>
        property>
    bean>


    <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
        <property name="basePackage" value="xyz.hongxiao2020.dao"/>
    bean>
  • 编码

    1. 实体

      package xyz.hongxiao2020.entity;
      
      import java.io.Serializable;
      
      public class User implements Serializable {
               
          private Integer id;
          private String name;
          private String password;
          ......
      
    2. 数据库创建表

    3. DAO接口

      package xyz.hongxiao2020.dao;
      
      import xyz.hongxiao2020.entity.User;
      
      public interface UserDao {
               
          public void save(User user);
      }
      
      
    4. Mapper文件配置

      
      
      
      <mapper namespace="xyz.hongxiao2020.dao.UserDao">
          <insert id="save" parameterType="User">
              insert into t_users(name,password) values (#{name},#{password})
          insert>
      mapper>
      

Spring与Mybatis整合细节

问题:Spring和Mybatis整合后,为什么DAO不提交事务,但是可以插入到数据库中?

本质上控制连接对象由Connection  => (连接池)DataSource

1.  Mybatis提供连接池对象 --> 创建Connection
Connection.setAutoCommit(false) 手工的控制了事务,操作完成后手工提交
手工的控制了事务,操作完成后手工提交
2. Durid (C3P0 DBCP) 作为连接池 -->创建了Connection
Connection.setAutoCommit(true) 默认自动提交事务,每执行一条sql,提交一次事务

答案:因为Spring和Mybatis整合时,引入了外部连接池对象,保持自动提交事务这个机制,不需要手工提交事务的操作

注意: 在未来的实战中,还会手工控制事务(多条sql一起成功,一起失败),后续通过Spring通过事务控制解决这个问题

Spring的事务处理

什么是事务:保证业务操作完整性的一种数据库机制

事务的四个特点:A C I D

  • A :原子性
  • C :一致性
  • I :隔离性
  • D :持久性
如歌控制事务
JDBC:
	Connection.setAutoCommit(false)
	Connection.commit();
	Connection.rollback();
Mybatis:
	Mybatis自动开启事务
	sqlsession(Connection).commit()
	sqlsession(Connection).rollback()
# 结论:控制事务的底层,都是通过Connection对象完成的
Spring控制事务的开发

Spring是通过AOP的方式进行事务开发

  <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-txartifactId>
      <version>5.1.4.RELEASEversion>
  dependency>
  1. 原始对象
public class xxxServiceImpl(){
     
	private xxxDAO xxxDAO;
	set, get
	1. 原始对象-->原始方法-->核心功能(业务处理+DAO调用)
	2. Dao作为Service的成员变量,通过依赖注入的方式进行赋值
}
  1. 额外功能
1. MethodInterceptor
public Object invoke(MethodInvocation invocation){
	Object ret = null;
	try{
		Connection.setAutoCommit(false);//开启事务
		ret = invocation.proceed()
		Connection.commit();//提交事务
	}catch(Exception e){
		Connection.rollback();//回滚
	}
	return ret
}
2. @Aspect
   	@Around
   
# spring封装了 DateSourceTransactionManager,进行事务管理但是我们需要为其注入连接(注入连接池DateSource)
  1. 切入点
@Transaction
事务的额外功能加给哪些业务方法

1. 类上:类中所有的方法都会加入事务
2. 方法上:这个方法会加入事务
  1. 组装切面
切入点
额外功能

<tx:annotation-driven transction-manger="dataSourceTransactionManger"/>
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
    
bean>
<bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="dateSourceTransactionManger" proxy-target-class/>

Spring事务属性

属性:描述物体特征的一系列值

事务属性:描述事务特征的一系列值

# 隔离属性
# 传播属性
# 只读属性
# 超时属性 
# 异常属性

如何添加事务属性

@Transactional(isolation = ,propagation = , readOnly = ,timeout = , rollbackFor = ,noRollbackFor = )

隔离属性:他描述了事务解决并发问题的特征

什么是并发?

多个事务,或者用户,在同一时间访问操作了相同的数据(同一时间:并不是说必须分毫不差,可能差0.00几秒)

并发会产生哪些问题?

  • 脏读
  • 不可重复读
  • 幻影读

并发问题如何解决?

通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题
隔离属性
脏读
一个事务,读取了另一个事务没有提交的数据,产生数据不一致的问题

解决办法:读已提交 @Transactional(isolation = Isolation.READ_COMMITTED)

不可重复读
一个事务,多次读取相同的数据,但是读取结果不一致,会在本事务中产生数据不一致的问题

注意:

  1. 不是脏读
  2. 在一个事务中

解决方法:@Transactional(isolation = Isolation.REPEATABLE_READ)

本质:一把行锁

幻影读

一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题。

解决方法:@Transactional(isolation = Isolation.SERIALIZABLE)

本质:表锁

总结

并发安全:SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED

运行效率:READ_COMMITTE>REPEATABLE_READ>SERIALIZABLE

数据库对隔离属性的支持

隔离属性的值 Mysql Oracle
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE

Oracle不支持REPEATABLE_READ值,如何解决不可重复读

采用多版本比对的方式,解决不可重复读的问题

默认隔离属性

ISOLATION_DEAFULT :会调用不同数据库所设置的默认隔离属性

Mysql:不可重复读 查询命令: SELECT @@tx_isolation;

Oracle:读已提交

隔离属性在实战中的建议:使用Spring的默认值就好

在未来的实战中,并发访问的几率很低

如果真的遇到并发问题,推荐使用乐观锁

Hibernate(JPA): Version Mybatis:通过拦截器自定义开发

传播属性(propagation)

传播属性:描述了事务解决嵌套问题的特征

什么是事务的嵌套:它指的是一个大的事务中,包含若干个小的事务

问题:大事务中融入了很多小的事务,他们彼此影响,最终会导致外部大的事务,丧失了事务的原子性

传播属性的值 外部不存在事务 外部存在事务 用法 备注
REQUIRED 开启新的事务 融入外部事务 @Transactional(propagation = Propagation.REQUIRED) 增删改方法
SUPPORTS 不开启事务 融入外部事务 @Transactional(propagation = Propagation.SUPPORTS) 查询方法
REQUIRES_NEW 开启新的事务 挂起外部事务,创建新事务 @Transactional(propagation = Propagation.REQUIRES_NEW) 日志记录
NOT_SUPPORTED 不开启事务 挂起外部事物 @Transactional(propagation = Propagation.NOT_SUPPORTED) 极其不常用
NEVER 不开启事务 抛出异常 @Transactional(propagation = Propagation.NEVER) 极其不常用
MANDATORY 抛出异常 融合到外部事务中 @Transactional(propagation = Propagation.MANDATORY) 极其不常用

Required是传播属性的默认值(增删改)

查询:显示指定传播属性为Supports

只读属性

针对于只进行查询操作的业务方法,可以加入只读属性,提高运行效率

默认值为false

@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public void login(String name, String password) {
     

}
超时属性
指定了事务等待的最长时间(单位:秒(s))

为什么事务要进行等待?

当前事务访问数据时,有可能访问的数据被其他事务加锁处理,那么此时本事务就必须进行等待

应用:@Transactional(timeout = 2) 
超时抛出异常

超时属性默认值为-1,最终由数据库指定(在开发中很少使用)

异常属性
Spring事务处理过程中
默认 对RuntimeException及其子类,采用的是回滚的机制
默认 对Exception及其子类,采用的是提交事务的机制
rollbackFor = {java.lang.Exception}  开启回滚 
noRollbackFor = {java.lang.RuntimeEexception} 关闭回滚
建议:实战中使用默认就可以了

总结:

1. 隔离属性  默认值
2. 传播属性	 Required(默认值)	增删改  Supports 查询
3. 只读属性	 默认值(false) 增删改  true  查询
4. 超时属性  默认值
5. 异常属性  默认值
增删改:@Transactional
查询:@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)

标签式事务配置

基于注解回顾:
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
    
bean>
<bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
bean>
@Transactional
public class UserServiceImpl implements UserService{
    private UserDao userDao;
......
<tx:annotation-driven transaction-manager="dateSourceTransactionManger" proxy-target-class="true"/>
基于标签

<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
bean>

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

<tx:advice id="txAdvice" transaction-manager="dateSourceTransactionManger">
    <tx:attributes>
        <tx:method name="register" isolation="DEFAULT" propagation="REQUIRED"/>
    tx:attributes>
tx:advice>

<aop:config>
    <aop:pointcut id="pc" expression="execution(* xyz.hongxiao2020.service.UserServiceImpl.register(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc">aop:advisor>
aop:config>
实战应用
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    bean>
    <bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>
    <tx:advice id="txAdvice" transaction-manager="dateSourceTransactionManger">
        <tx:attributes>
            <tx:method name="register"/>
            <tx:method name="modify*">tx:method>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        tx:attributes>
    tx:advice>

    <aop:config>
        <aop:pointcut id="pc" expression="execution(* xyz.hongxiao2020.service..*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc">aop:advisor>
    aop:config>

Spring整合MVC框架

  1. 依赖:
<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>javax.servlet-apiartifactId>
    <version>3.1.0version>
    <scope>providedscope>
dependency>
<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>jstlartifactId>
    <version>1.2version>
dependency>
<dependency>
    <groupId>javax.servlet.jspgroupId>
    <artifactId>javax.servlet.jsp-apiartifactId>
    <version>2.3.1version>
    <scope>providedscope>
dependency>

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.1.4.RELEASEversion>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjrtartifactId>
    <version>1.8.8version>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.8.3version>
dependency>

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.1.4.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-txartifactId>
    <version>5.1.4.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webartifactId>
    <version>5.1.4.RELEASEversion>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>5.1.48version>
dependency>
<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.4.6version>
dependency>
<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatis-springartifactId>
    <version>2.0.2version>
dependency>
<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druidartifactId>
    <version>1.1.18version>
dependency>

<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-log4j12artifactId>
    <version>1.7.25version>
dependency>
<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>
  1. 为什么要整合MVC
1. MVC框架提供了控制器(Controller)调用Service
  dao-> service->controller
2. 请求响应的处理
3. 接收请求参数
4. 控制程序的运行流程
5. 试图解析 (JSP JSON Freemarker Thyemeleaf)
  1. Spring可以整合那些MVC框架

    1. struts1
    2. webwork
    3. jsf
    4. struts2
    5. springMVC
    
  2. Spring整合MVC框架的核心思想

    1. 准备工厂
# 1. Web开发过程中如何创建工厂
	ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
							   WebXmlApplicationContext()
# 2.如何保证工厂唯一且被共用
	被共用:Web request|session|ServletContext(application)
	工厂存储在ServletContext这个作用域中,ServletContext.setAttritube("xxxx",ctx);
	唯一:ServletContext对象创建的同时执行new ClassPathXmlApplicationContext("/applicationContext.xml");
	ServletContextlistener在ServletContext对象创建的同时,被调用(只会被调用一次),把工厂创建的代码写在ServletContextListener中,也会保证只调用一次,最终工厂就保证了唯一性
# 3.总结
	在开发和创建工厂的过程中,既要创建工厂同时又要保证工厂的唯一和被大家所共用。
	
	ServletContextListener(唯一)
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        ServletContext.setAttritube("xxxx",ctx);
# 4.spring中封装了ContextLoaderListener
	1. 创建工厂
	2. 把工厂存储在ServletContext中
ContextLoaderListener使用方式
web.xml
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
 
配置文件位置
<context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>classpath:applicationContext.xmlparam-value>
context-param>      
  1. 代码整合
 依赖注入: 把Service对象注入给控制器对象
Spring与struts2整合
  1. Spring与Struts2整合思路分析

    1. Struts2中的Action需要通过Spring的以来注入获得Service对象
    
  2. Spring与Struts2整合的编码实现

    • 搭建开发环境

      • 引入相关jar(Spring Struts2)

        <dependency>
            <groupId>org.apache.strutsgroupId>
            <artifactId>struts2-spring-pluginartifactId>
            <version>2.3.8version>
        dependency>
        
      • 引入对应的配置文件

        applicationContext.xml

        struts.xml

        log4j.properties

      • 初始化配置

        Spring(ContextLoaderListener -> web.xml)

        Structs2(Filter -> web.xml)

       
       <web-app>
         <display-name>Archetype Created Web Applicationdisplay-name>
         <listener>
           <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
         listener>
         
         <context-param>
           <param-name>contextConfigLocationparam-name>
           <param-value>classpath:applicationContext-*.xmlparam-value>
         context-param>
         
         <filter>
           <filter-name>struts2filter-name>
           <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
         filter>
         <filter-mapping>
           <filter-name>struts2filter-name>
      <url-pattern>/*url-pattern>
         filter-mapping>
    web-app>
 - 编码

   - 开发service对象
 
     最终在spring配置文件中创建service对象
    <bean id="userService" class="xyz.hongxiao2020.struts2.UserServiceImpl"/>
   - 开发Action对象
 
     - 开发类
           import com.opensymphony.xwork2.Action;
           public class RegAction implements Action {
     
               private UserService userService;
           
               public UserService getUserService() {
     
                   return userService;
               }
               public void setUserService(UserService userService) {
     
                   this.userService = userService;
               }
               @Override
               public String execute() throws Exception {
     
                   userService.register();
              return Action.SUCCESS;
               }
      }
     - Spring(applicationContext.xml)
           <bean id="userService" class="xyz.hongxiao2020.struts2.UserServiceImpl"/>
      <bean id="regAction" class="xyz.hongxiao2020.struts2.RegAction" scope="prototype">
               <property name="userService" ref="userService"/>
      bean>
     - Struts2(struts.xml)
           <struts>
               <package name="ssm" extends="struts-default">
                url   reg.action --->接收到用户的请求后,创建RegAction对象,进行相应的处理
                   <action name="reg" class="regAction">
                       <result name="success">/index.jspresult>
                   action>
               package>
           struts>                
Spring+Struts+Mybatis整合(SSM)
  1. 思路分析

    SSM = Spring+Struts Spring+Mybatis

  2. 整合编码

  • 搭建开发
    - 引入相关jar(Spring Struts2 Mybatis)

    
    <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>3.1.0version>
            <scope>providedscope>
    	dependency>
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>jstlartifactId>
            <version>1.2version>
        dependency>
        <dependency>
            <groupId>javax.servlet.jspgroupId>
            <artifactId>javax.servlet.jsp-apiartifactId>
            <version>2.3.1version>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.4.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjrtartifactId>
            <version>1.8.8version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.8.3version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-jdbcartifactId>
            <version>5.1.4.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-txartifactId>
            <version>5.1.4.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webartifactId>
            <version>5.1.4.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.apache.strutsgroupId>
            <artifactId>struts2-spring-pluginartifactId>
            <version>2.3.8version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.48version>
        dependency>
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.4.6version>
        dependency>
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatis-springartifactId>
            <version>2.0.2version>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.18version>
        dependency>
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
            <version>1.7.25version>
        dependency>
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.17version>
        dependency>
    
    • 引入对应的配置文件

    applicationContext.xml

    struts.xml

    log4j.properties

    mybatis-config.xml xxxMapper.xml

    • 初始化配置

    • Spring(ContextLoaderListener -> web.xml)

    • Struts(Filter -> web.xml)

      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
      listener>
      
      <context-param>
          <param-name>contextConfigLocationparam-name>
          <param-value>classpath:applicationContext.xmlparam-value>
      context-param>
      
      <filter>
          <filter-name>struts2filter-name>
          <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
      filter>
      <filter-mapping>
          <filter-name>struts2filter-name>
          <url-pattern>/*url-pattern>
      filter-mapping>
    
  • 编码

    • DAO

      1. 配置文件的配置
      	1. DataSource
      	2. SqlSessionFactory ---> SqlSessionFactoryBean
      		1. dataSource 
      		2.typeAliasesPackage
      		3. mapperLocations
      	3. MapperScannerCobfigur  --->DAO接口实现
      2. 编码
      	1. entity
      	2. table
      	3. DAO接口
      	4. 实现Mapper文件
      
  • Service (Spring添加事务)

    1. 原始对象 ---> 注入DAO
    2. 额外功能 ----> DataSourceTransactionMapper --->dataSource
    3. 切入点 + 事务属性 @Transactional()
    4. 组装切面
    
  • Controller (Spring+struts2)

    1. 开发控制器 implements Action 注入Service
    2. Spring配置文件
    	1. 注入service
    	2. scope="prototype"
    3. struts.xml
    
    
  • 完整代码

    reg.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
    
        Title
    
    
    
    UserName:
    PassWord:

    regOk.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
    
    
        

    regOk

struts.xml

    
    
    <struts>
        <package name="ssm" extends="struts-default">
            <action name="reg" class="regAction">
                <result name="success">/regOk.jspresult>
            action>
        package>
	 struts>

applicationContext.xml

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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">
        
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost/pzh?useSSL=false"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        bean>
    
        
        <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="typeAliasesPackage" value="xyz.hongxiao2020.entity"/>
            <property name="mapperLocations">
                <list>
                    <value>classpath:xyz.hongxiao2020.mapper/*Mapper.xmlvalue>
                list>
            property>
        bean>
    
        
        <bean id="configure" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
            <property name="basePackage" value="xyz.hongxiao2020.dao"/>
        bean>

    <bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
        <property name="userDao" ref="userDao">property>
    bean>
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    <bean id="regAction" class="xyz.hongxiao2020.action.RegAction" scope="prototype">
        <property name="userService" ref="userService"/>
    bean>
    beans>
  1. spring开发过程中多配置文件的处理

    spring会根据需要,把配置信息分门别类的放置在多个配置文件中,便于后续的管理及维护

    DAO 		---> applicationContext-dao.xml
    Service     ---> applicationContext-service.xml
    Action      ---> applicationContext-action.xml
    注意:虽然提供了多个配置文件,但是后续应用过程中,还要进行整合
    
    • 通配符方式

      1. 非web环境
      ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-*.xml");
      2. web环境
      
      	contextConfigLocation
      	classpath:applicationContext-*.xml
      
      
    • applicationContext.xml 目的整合其他配置内容
      <import resource="applicationContext-dao.xml"/>
      <import resource="applicationContext-service.xml"/>
      <import resource="applicationContext-action.xml"/>
      

注解编程

  1. 指的是在类或者方法上面加入特定的注解(@XXX),完成特定的功能开发
@Component
public class XX()
  1. 为什么要使用注解编程

    1. 注解开发方便
    	代码简洁 开发速度大大提高
    2. Spring开发潮流
    	Spring2.x引入注解,spring3.x完善注解 SpringBoot普及推广注解编程
    
  2. 注解的作用

    • 替换XML这种配置形式,简化配置

      @Component
      public class User{
      
      }
      代替了
       
      
    • 替换接口,实现调用双方的契约性

      通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式来完成。
      
  3. spring注解的发展历程

    1. Spring2.x开始支持注解编程 @Component  @Service @Scope
    	目的:提供这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充。
    2. Spring3.x @Configuration  @Bean...
    	目的:彻底替换xml,基于纯注解编程
    3. Spring4.x SpringBoot
    	提倡使用注解常见开发
    
  4. Spring注解开发的一个问题

    Spring基于注解进行配置后,还能否解耦合呢?
    在Spring框架应用注解时,如果对注解配置的内容不满意,可以通过Spring的配置文件进行覆盖的。
    
Spring的基础注解(Spring2.x)

这个阶段的注解,仅仅是简化xml的配置,并不能完全替代xml

1. 对象创建相关注解
  • 搭建开发环境

    
    作用:让spring框架在设置的包及其子包中扫描对应的注解,使其生效
    
  • 对象创建相关注解

    • @Component

      作用:替换原有Spring配置文件中的
    • Component细节

      • 如何显示的指定工厂创建对象的id值

        @Component("u")
        
      • Spring配置文件覆盖注解配置内容

        applicationContext.xml
        
        <bean id="u" class="xyz.hongxiao2020.bean.User">
            <property name="id" value="10"/>
        bean>
        id class 要和注解中的设置保持一致
        
    • Component衍生注解

      @Repository  --->XXXDAO
      @Service	 --->XXXService
      @Controller  --->XXXAction
      
      注意:本质上这些衍生注解就是@Component 作用 细节 用法都是完全一样的
      
      目的:更准确表达一个类型的作用
      
      注意:Spring整合Mybatis的开发过程中,不使用@Repository @Component
      
  • @Scope注解

    作用:控制简单对象的创建次数
    注意:不添加@Scope注解 Spring默认值是singleton
    
    
  • @Lazy注解

    作用:延迟创建单实例对象
    注意:一旦使用了@Lazy注解后,Spring会在使用这个对象的时候,进行这个对象的创建
    
    
  • 生命周期方法相关注解

    1. 初始化相关方法 @PostConstruct
    	InitializingBean
    	
    2. 销毁方法	@PreDestroy
    	DisposableBean
    	
    	
    @PostConstruct
    public void myInit(){
    	System.out.println("User.myInit");
    }
    @PreDestroy
    public void myDestroy(){
    	System.out.println("User.myDestroy");
    }
    
    注意: 1. 上述的两个注解并不是Spring提供的,JSR(JavaEE规范)520
    	  2. 再一次的验证,通过注解实现了接口的契约性
    
2. 注入相关注解
  • 用户自定义类型 @Autowired

    @Service
    public class UserServiceImpl implements UserService {
           
        private UserDAO userDAO;
        @Autowired
        public void setUserDAO(UserDAO userDAO) {
           
            System.out.println("UserServiceImpl.setUserDAO");
            this.userDAO = userDAO;
        }
        public void register(User user) {
           
            userDAO.save(user);
        }
    }
    
    
    @Repository
    public class UserDAOImpl implements UserDAO {
           
        private  User user;
    
        @Autowired
        public void setUser(User user) {
           
            this.user = user;
        }
    
        public void save(User user) {
           
            System.out.println("UserDAOImpl.save");
        }
    }
    
    @Autowired细节
    1. Autowired注解是基于类型进行注入 [推荐]
    	基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类
    2. Autowired Qualifier 基于名字进行注入
    	基于名字的注入:注入对象的id值,必须与Qualifier注解中设置的名字相同
    3. Autowired注解放置位置
    	a)放置在对应成员变量的set方法上
    	b)直接把这个注解放置在成员变量上,spring直接通过反射对成员变量进行注入(赋值)(没有调用set方法)[推荐]
    4. JavaEE规范中类似功能的注解
    	JSR250 @Resource(name="") 基于名字进行注入 
    	注意:如果在应用Resource注解时,名字没有配对成功,那么它会继续按照类型进行注入
    	
    	JSR330 @Inject 作用和@Autowired完全一致 基于类型进行注入 --->EJB3.0
    	
            javax.inject
            javax.inject
            1
    	
    
    @Service
    public class UserServiceImpl implements UserService {
           
        private UserDAO userDAO;
        @Autowired
        @Qualifier("userDAOImpl2")
        public void setUserDAO(UserDAO userDAO) {
           
            System.out.println("UserServiceImpl.setUserDAO");
            this.userDAO = userDAO;
        }
        public void register(User user) {
           
            userDAO.save(user);
        }
    }
    
    
    @Repository
    public class UserDAOImpl implements UserDAO {
           
        private  User user;
    
        @Autowired
        public void setUser(User user) {
           
            this.user = user;
        }
    
        public void save(User user) {
           
            System.out.println("UserDAOImpl.save");
        }
    }
    
    @Repository
    public class UserDAOImpl2 implements UserDAO {
           
        private User user;
    
        @Autowired
        public void setUser(User user) {
           
            this.user = user;
        }
    
        public void save(User user) {
           
            System.out.println("UserDAOImpl2.save");
        }
    }
    
  • JDK类型

    @Value注解完成
    1. 设置xxx.properties
    	id = 9
    	name = pzh
    	password = 123
    2. Spring工厂读取配置文件
    	
    3. 代码
    属性 @Value("${id}")
    public class User implements Serializable {
        @Value("${id}")
        private Integer id;
        @Value("${name}")
        private String name;
        @Value("${password}")
        private String password;
    
    • @PropertySource
    1. 作用:用于替换配置文件中的标签
    2. 开发步骤
    	1. 设置xxx.properties
    	2. 应用@PropertySource
                @Component
                @Lazy
                @PropertySource("xxx.properties")
                public class User implements Serializable {
                    @Value("${id}")
                    private Integer id;
                    @Value("${name}")
                    private String name;
                    @Value("${password}")
                    private String password;
                    ......
    	
    	3. 代码 @Value("${}")
    
    • @Value注解使用细节

      • @Value注解不能应用在静态成员变量上

        如果应用赋值和注入将失败
        
      • @Value注解+properties这种方法不能注入集合类型

        Spring提供新的配置形式 YAML YML (SpringBoot)
        
3. 注解扫描详解
 
 当前包 及其子包
  1. 排除方式

    
        
     
    1. assignable:排除特定的类型    
    
    2. annotation:排除特定的注解
    
    3. aspectj:排除包切入点或者类切入点表达式
    
    
    4. regex:排除正则表达式
    5. custom:自定义排除策略,框架底层开发
    
    注意:排除策略可以叠加使用
    
  2. 包含方式

    
            
    
    1. use-default-filters="false"
    	作用:让spring默认的注解扫描方式失效
    2. 
    	作用:指定需要扫描的路径
    	type作用方式一样
    注意:包含也支持叠加策略
    
4. 对于注解开发的思考
  • 配置互通

    Spring注解配置 配置文件的配置 互通
    
    @Repository
    public class UserDaoImpl{
    
    }
    
    public class UserServiceImpl{
    	private UserDao userDao;
    	set;
    } 
    
    
    	
    
    
  • 什么情况下使用注解 什么情况下使用配置文件

@Component 替换
基础注解(@Component @Autowired @Value) 程序员开发类型的配置

1. 在程序员开发的类型上 可以加入对应的注解 进行对象的创建

2. 应用其他非程序员开发的类型时,还是需要使用标签进行配置的(SqlSessionFactoryBean MapperScannerConfigurer)
5. SSM整合开发(半注解开发)
  • 搭建开发环境
    • 引入相关jar [SSM pom]

    • 引入相关配置文

      • applicationContext.xml
      • struts.xml
      • log4j.properties
      • xxxMapper.xml
    • 初始化配置

      • web.xml Spring(ContextLoaderListener)
      • web.xml Struts Filter
    • 编码

      <context:component-scan base-package="xyz.hongxiao2020"/>
      
    • DAO(Spring+Mybatis)

      1. 配置文件的配置
      	1. DataSource
      	2. SqlSessionFactory ---> SqlSessionFactoryBean
      		1. dataSource 
      		2.typeAliasesPackage
      		3. mapperLocations
      	3. MapperScannerConfigur  --->DAO接口实现
      2. 编码
      	1. entity
      	2. table
      	3. DAO接口
      	4. 实现Mapper文件
      
    • Service

      1. 原始对象 ---> 注入DAO
      	@Service --->@Autowired
      2. 额外功能 ----> DataSourceTransactionMapper --->dataSource
      3. 切入点 + 事务属性 @Transactional()
      4. 组装切面
    • Controller(Spring+struts2)

      1.  @Controller
          @Scope("prototype")
          public class RegAction implements Action {
              private User user;
              private UserService userService;
      
              @Autowired
              public void setUserService(UserService userService) {
                  this.userService = userService;
              }
      
              @Override
              public String execute() throws Exception {
                  userService.register(user);
                  return Action.SUCCESS;
              }
          }
      
      2. struts.xml
       
Spring的高级注解(Spring3.x及以上)
1. 配置Bean
Spring在3.x提供的新的注解,用于替换xml配置文件
@Configuration
public class AppConfig{
}
1. @Configuration的出现时为了替换原来的application.xml等文件
2. AnnotationConfigApplicationContext
	1. 创建工厂代码
		ApplicationContext ctx = new AnnotationConfigApplicationContext();
	2. 指定配置文件
		1. 指定配置bean的Class
			ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
		2. 指定配置bean所在的路径
			ApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020");
  • 配置Bean开发的细节分析

    • 基于注解开发使用日志

      1. 不能集成Log4j
      2. 集成logback
      
      • 引入相关jar包

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>1.7.25version>
        dependency>
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>jcl-over-slf4jartifactId>
            <version>1.7.25version>
        dependency>
        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-coreartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
            <groupId>org.logback-extensionsgroupId>
            <artifactId>logback-ext-springartifactId>
            <version>0.1.4version>
        dependency>
        
      • 引入logback配置文件

        
        
        <configuration>
        
            <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
                <encoder>
        
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
                encoder>
            appender>
        
            <root>
                <level value="DEBUG" />
                <appender-ref ref="STDOUT" />
            root>
        configuration>
        
    • @Configuration注解的本质

      本质:也是@Component注解的衍生注解
      可以应用
2. @Bean注解
@Bean注解在配置bean中进行使用,等同于XML配置文件中的标签
  1. @Bean注解的基本使用
  • 对象的创建

    1. 简单对象
    	直接通过new方式创建的对象
    	
    	@Configuration
        public class AppConfig {
            @Bean
            public User user(){
                return new User();
            }
        }
        方法名对应id
        
    2. 复杂对象
    	不能通过new的方式直接创建的对象
    	
    	@Configuration
        public class AppConfig {
            @Bean
            public Connection conn(){
                Connection conn = null;
                try {
                    Class.forName("com.mysql.jdbc.Driver");
                    conn = DriverManager.getConnection("jdbc:mysql://localhost/pzh?useSSL=false", "root", "root");
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                return conn;
            }
        }
    
    与FactoryBean集成
    public class ConnectionFactoryBean implements FactoryBean {
        public Connection getObject() throws Exception {
            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/pzh?useSSL=false", "root", "root");
            return conn;
        }
    
        public Class getObjectType() {
            return Connection.class;
        }
    
        public boolean isSingleton() {
            return false;
        }
    }
    
    @Configuration
    public class AppConfig {
        @Bean
        public Connection conn(){
            Connection connection = null;
            try {
                ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
                connection = factoryBean.getObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return connection;
        }
    }
    
  • 自定义对象id值

    @Bean("id")
    
  • 控制对象创建次数

    @Configuration
    public class AppConfig {
           
        @Bean("u")
        @Scope("prototype")
        public User user (){
           
            return new User();
        }
    }
    
  1. @Bean注解的注入
  • 用户自定义类型

    //参数注入
    @Configuration
    public class AppConfig {
           
        @Bean
        public UserDAO userDAO(){
           
            return new UserDAOImpl();
        }
    
        @Bean
        public UserService userService(UserDAO userDAO){
           
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO);
            return userService;
        }
    }
    //简化写法
    @Configuration
    public class AppConfig {
           
        @Bean
        public UserDAO userDAO(){
           
            return new UserDAOImpl();
        }
    
        @Bean
        public UserService userService(){
           
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO());
            return userService;
        }
    }
    
  • JDK类型的注入

     @Bean
        public User user(){
           
            User user = new User();
            user.setId(1);
            user.setName("pzh");
            return user;
        }
    
    • JDK类型注入的细节分析

      //代码中进行set方法的调用存在耦合
      
      @Configuration
      @PropertySource("classpath:/init.properties")
      public class AppConfig {
               
          @Value("${id}")
          private Integer id;
          @Value("${name}")
          private String  name;
      
          @Bean
          public User user(){
               
              User user = new User();
              user.setId(id);
              user.setName(name);
              return user;
          }
      }
      
3.@ComponentScan注解
@ComponentScan注解在配置bean中使用,等同于xml配置文件中的标签

目的:进行相关注解的扫描 (@Component @Value .. @Autowired)
  1. 基本使用

    @Configuration
    @ComponentScan(basePackages = "xyz.hongxiao2020.scan")
    public class AppConfig {
           
        ...
    }
    
    <context:component-scan base-package="">
    
  2. 排除、包含的使用

  • 排除

    @Configuration
    @ComponentScan(basePackages = "xyz.hongxiao2020.scan",
                   excludeFilters = {
           @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Service.class),
     								 @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = {
           "xyz.hongxiao2020..*"})})
    public class AppConfig1 {
           
    
    }
    
    type=FilterType.ANNOTATION          value
        			ASSIGNABLE_TYPE     value
        			ASPECTJ				pattern
        			REGEX               pattern
        			CUSTOM				value
    
   
- 包含

   ```java
   @Configuration
   @ComponentScan(basePackages = "xyz.hongxiao2020.scan",
                  useDefaultFilters = false,
                  includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class})})
   public class AppConfig1 {
   
   }
   
4.Spring工厂创建对象的多种配置方式
  1. 多种配置方式的应用场景

    1. @Component 衍生 @Autowired 程序员自己开发的类型上 UserService UserDAO Controller...
    2. @Bean 框架提供的类型,别的程序员开发的类型(没有源码)SqlSessionFactoryBean MapperScannerConfigure
    3. 纯注解的开发过程中,基本不用,遗留系统的整合
    4. @Import注解 @Configuration
    			  @Import(User.class)  --->得到对象 xyz.hongxiao2020.entity.User
       @Import 1.Spring框架的底层使用
       		   2.多配置bean整合
    
  2. 配置的优先级

    1. @Component及其衍生注解 < @Bean < 配置文件bean标签
    2. 优先级高的配置 覆盖优先级低的配置 (id值保持一致)配置文件与注解整合@ImportResource("applicationContext.xml")
    
  • 解决基于注解进行配置的耦合问题

    优先级覆盖 xml覆盖注解(相同id)
    
    @Configuration
    @ComponentScan(basePackages = "xyz.hongxiao2020")
    public class AppConfig {
           
        @Bean
        public UserDAO userDAO(){
           
            return new UserDAOImpl();
        }
    }
    
    @Configuration
    @ComponentScan(basePackages = "xyz.hongxiao2020")
    @ImportResource("applicationContext.xml")
    public class AppConfigNew {
           
        @Bean
        public UserDAO userDAO(){
           
            return new UserDAOImpl();
        }
    }
    
    //applicationContext.xml
    //
    
    //        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class,AppConfigNew.class);
    //        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020");
    
    
5. 整合多个配置信息
  • 为什么会有多个配置信息

    拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想
    
  • 多配置信息整合的方式

    • 多个配置Bean的整合
    • 配置Bean与@Component相关注解的整合
    • 配置Bean与Spring XML配置文件的整合
  • 整合多种配置需要关注那些要点

    • 如何使多配置信息汇总成一个整体
    • 如何实现跨配置的注入
  1. 多个配置Bean的整合
  • 多配置的信息汇总

    • base-package进行多个配置Bean的整合

      AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020.config");
      
      
    • @Import

      1. 可以创建对象
      2. 多配置bena的整合
      
      @Configuration
      @Import(AppConfig2.class)
      public class AppConfig1 {
               
          @Bean
          public User user(){
               
              return new User();
          }
      }
      
    • 在工厂创建时指定多个配置Bean的class对象

              AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class, AppConfig2.class);
      
      
  • 跨配置进行注入

    //在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加入@Autowired注解完成的
    @Configuration
    public class AppConfig2 {
           
        @Bean
        public UserDAO userDAO(){
           
            return new UserDAOImpl();
        }
    }
    
    @Configuration
    @Import(AppConfig2.class)
    public class AppConfig1 {
           
    
        @Autowired
        private UserDAO userDAO;
    
        @Bean
        public UserService userService(){
           
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO);
            return userService;
        }
    }
    
  1. 配置Bean与@Component相关注解的整合

    @Configuration
    @ComponentScan(basePackages = "xyz.hongxiao2020.dao")
    public class AppConfig1 {
           
        @Autowired
        private UserDAO userDAO;
    
        @Bean
        public UserService userService(){
           
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO);
            return userService;
     }
    }
    
    
  2. 配置Bean与配置文件整合

    1. 遗留系统的整合
    2. 配置覆盖
    
    @Configuration
    @ImportResource("applicationContext.xml")
    public class AppConfig1 {
           
    
        @Autowired
        private UserDAO userDAO;
    
        @Bean
        public UserService userService(){
           
            UserServiceImpl userService = new UserServiceImpl();
            userService.setUserDAO(userDAO);
            return userService;
        }
    }
    
6. 配置Bean的底层实现原理
Spring在配置Bean中加入@Configuration注解后,底层就会通过Cglib的代理方式,进行对象相关的配置、处理
7. 四维一体的开发思想
  1. 什么是四维一体

    Spring开发一个功能的四种形式,虽然开发方式不同,但最终效果是一样的
    1. 基于schema
    2. 基于特定功能注解
    3. 基于原始
    4. 基于@Bean注解
    
  2. 四维一体的开发案例

    1. 
    2. @PropertySource("four.properties")   【推荐】
    3. 
            
       
    4.  @Bean
        public PropertySourcesPlaceholderConfigurer configurer(){
            PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
            configurer.setLocation(new ClassPathResource("four.properties"));
            return configurer;
        }  【推荐】
    
8. 纯注解版AOP编程
  1. 搭建环境

    1. 应用配置Bean
    2. 注解扫描
    
  2. 开发步骤

    1. 原始对象
        @Service
        public class UserServiceImpl implements UserService {
            public void register() {
                System.out.println("UserServiceImpl.register");
            }
    
            public void login() {
                System.out.println("UserServiceImpl.login");
            }
        }
    2. 创建切面类 (额外功能 切入点 组装切面)
        @Aspect
        @Component
        public class MyAspect {
            @Pointcut("execution(* xyz.hongxiao2020.aop..*.*(..))")
            public void pointCut(){
    
            }
    
            @Around("pointCut()")
            public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
                System.out.println("-----log------");
                Object proceed = joinPoint.proceed();
                return proceed;
            }
        }
    3. Spring的配置文件中
    	替换为
    	@EnableAspectJAutoProxy --->配置Bean
    	
    	
            @Configuration
            @ComponentScan(basePackages = {"xyz.hongxiao2020.aop"})
            @EnableAspectJAutoProxy
            public class AppConfig {
    
            }
    
  3. 注解AOP细节分析

    1. 代理创建方式的切换  JDK Cglib
    	
    	@EnableAspectJAutoProxy(proxyTargetClass)
    2. SpringBoot AOP开发方式
    	@EnableAspectJAutoProxy  已经设置好了
            1. 原始对象
            @Service
            public class UserServiceImpl implements UserService {
                public void register() {
                    System.out.println("UserServiceImpl.register");
                }
    
                public void login() {
                    System.out.println("UserServiceImpl.login");
                }
            }
            2. 创建切面类 (额外功能 切入点 组装切面)
                @Aspect
                @Component
                public class MyAspect {
                    @Pointcut("execution(* xyz.hongxiao2020.aop..*.*(..))")
                    public void pointCut(){
    
                    }
    
                    @Around("pointCut()")
                    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
                        System.out.println("-----log------");
                        Object proceed = joinPoint.proceed();
                        return proceed;
                    }
                }
                Spring    AOP   代理默认实现 JDK 
                SpringBoot   AOP  代理默认实现  Cglib
    
9.纯注解Spring+Mybatis整合
  • 基础配置

    1. 连接池
    2. SqlSessionFactory
    3. MapperScannerConfigure
    
  • 编码

    @Configuration
    @ComponentScan(basePackages = {
           "xyz.hongxiao2020.mybatis"})
    @MapperScan(basePackages = {
           "xyz.hongxiao2020.mybatis"})
    public class MybatisAutoConfiguration {
           
        @Bean
        public DataSource dataSource(){
           
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost/pzh?useSSL=false");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
            return dataSource;
        }
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
           
            SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource);
            sessionFactoryBean.setTypeAliasesPackage("xyz.hongxiao2020.mybatis");
            sessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
            return sessionFactoryBean;
        }
    }
    
    
    1. MapperLocations编码时通配的写法

      //设置Mapper文件路径
      sessionFactoryBean.setMapperLocations(Resource..);
      Resource resource = new ClassPathResource("UserDAOMapper.xml");
      
      sessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
      
      /*
       
                  
                      classpath:xyz.hongxiao2020.mybatis.mapper/*Mapper.xml
                  
       
       */
      PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
      Resource[] resources = resolver.getResources("xyz.hongxiao2020.mybatis.mapper/*Mapper.xml");
      sessionFactoryBean.setMapperLocations(resources);
      
    2. 配置Bean数据耦合的问题

      mybatis.properties
      mybatis.driverClassName = com.mysql.jdbc.Driver
      mybatis.url = jdbc:mysql://localhost/pzh?useSSL=false
      mybatis.username = root
      mybatis.password = root
      mybatis.typeAliasesPackages = xyz.hongxiao2020.mybatis
      mybatis.mapperLocations = xyz.hongxiao2020.mybatis.mapper/*Mapper.xml
      
      @Component
      @PropertySource("classpath:/mybatis.properties")
      public class MybatisProperties {
               
          @Value("${mybatis.driverClassName}")
          private String driverClassName;
          @Value("${mybatis.url}")
          private String url;
          @Value("${mybatis.username}")
          private String username;
          @Value("${mybatis.password}")
          private String password;
          @Value("${mybatis.typeAliasesPackages}")
          private String typeAliasesPackages;
          @Value("${mybatis.mapperLocations}")
          private String mapperLocations;
      
          public String getDriverClassName() {
               
              return driverClassName;
          }
          ......
      
      @Configuration
      @ComponentScan(basePackages = {
               "xyz.hongxiao2020.mybatis"})
      @MapperScan(basePackages = {
               "xyz.hongxiao2020.mybatis"})
      public class MybatisAutoConfiguration {
               
      
          @Autowired
          private MybatisProperties mybatisProperties;
          @Bean
          public DataSource dataSource(){
               
              DruidDataSource dataSource = new DruidDataSource();
              dataSource.setDriverClassName(mybatisProperties.getDriverClassName());
              dataSource.setUrl(mybatisProperties.getUrl());
              dataSource.setUsername(mybatisProperties.getUsername());
              dataSource.setPassword(mybatisProperties.getPassword());
              return dataSource;
          }
      
          @Bean
          public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
               
              SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
              sessionFactoryBean.setDataSource(dataSource);
              sessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackages());
      //        sessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
              try {
               
                  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                  Resource[] resources = resolver.getResources(mybatisProperties.getMapperLocations());
                  sessionFactoryBean.setMapperLocations(resources);
              } catch (IOException e) {
               
                  e.printStackTrace();
              }
              return sessionFactoryBean;
      
          }
      }
      
10. 纯注解版事务编程
1. 原始对象

	


    @Service
    @Transactional
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDAO userDAO;
        public UserDAO getUserDAO() {
            return userDAO;
        }

        public void setUserDAO(UserDAO userDAO) {
            this.userDAO = userDAO;
        }

2. 额外功能

	


    @Configuration
    @EnableTransactionManagement
    public class TransactionAutoConfiguration {
        @Autowired
        DataSource dataSource;
        @Bean
        public DataSourceTransactionManager dataSourceTransactionManager(){
            DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
            transactionManager.setDataSource(dataSource);
            return transactionManager;
        }
    }


3. 事务属性
@Transactional
public class UserServiceImpl implements UserService{
	private UserDAO userDAO;
}

    	@Service
        @Transactional
        public class UserServiceImpl implements UserService {
            @Autowired
            private UserDAO userDAO;
            public UserDAO getUserDAO() {
                return userDAO;
            }

            public void setUserDAO(UserDAO userDAO) {
                this.userDAO = userDAO;
            }
4. 基于Schema的事务配置



    @Configuration
    @EnableTransactionManagement
    public class TransactionAutoConfiguration {
        @Autowired
        DataSource dataSource;
        @Bean
        public DataSourceTransactionManager dataSourceTransactionManager(){
            DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
            transactionManager.setDataSource(dataSource);
            return transactionManager;
        }
    }
1. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020.mybatis");
SpringBoot 实现思想
2. SpringMybatis --->DAO  事务基于注解 --> Service  Controller
org.springframework.web.context.ContextLoaderListener 只能使用xml创建工厂 无法提供   AnnotationConfigApplicationContext

11. Spring框架中YML的使用
  1. 什么是YML

    YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大
    
  2. Properties进行配置的问题

    1. Properties表达过于繁琐,无法表达数据的内在联系
    2. Properties无法表达对象、集合类型
    
  3. YML语法简介

    1. 定义yml文件
    	xxx.yml xxx.yaml
    2. 语法
    	1. 普通语法
    		name:value
    	2. 对象概念(空格)
    		account:
    			id:1
    			name:222
    	3. 定义集合
    		service:
    			- 1111
    			- 2222
    
  4. Spring与YML集成思路的分析

    1. 准备配置文件
    	init.yml
    	name:root
    	password:root
    2. 读取yml 转换成 Properties
    	YamlPropertiesFactoryBean.setResources(yml配置文件路径) new ClassPathResource();
    	YamlPropertiesFactoryBean.getObject() ---> Properties
    3. 应用PropertySourcePlaceholderConfigurer
    	PropertySourcePlaceholderConfigurer.setProperties()
    4. 类中 @Value注解 注入
    
  5. Spring与YML集成编码

  • 环境搭建

    <dependency>
        <groupId>org.yamlgroupId>
        <artifactId>snakeyamlartifactId>
        <version>1.23version>
    dependency>
    最低版本 1.18
    
  • 编码

    1. 准备yml配置文件
    2. 配置Bean中操作 完成YAML读取 与 PropertySourcePlaceholderConfigure的创建
    	@Bean
        public PropertySourcesPlaceholderConfigurer configurer(){
            YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
            yamlPropertiesFactoryBean.setResources(new ClassPathResource("init.yml"));
            Properties properties = yamlPropertiesFactoryBean.getObject();
            PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
            configurer.setProperties(properties);
            return configurer;
        }
    3. 类 加入 @Value注解
    
  1. Spring与YML集成的问题

    1. 集合处理问题
    	SpringEL表达式解决
    	@Value("#{'${list}'.split(',')}")
    2. 对象类型的YML进行配置时 过于繁琐
    	@Value("${account.name}")
    SpringBoot  @ConfigurationProperties
    

你可能感兴趣的:(spring)