循环依赖

A依赖B B依赖A

  • 问题产生
    • 先创建A对象并实例化,此时A对象中的B对象属性为空,填充B属性
    • 当容器中没有B对象则使其实例化,此时B对象中的A对象属性为空,填充A
    • 从容器中找不到则创建
    • 致此就形成循环依赖问题
  • 解决问题

    • 解决问题的核心问题则在于实例化与初始化分开进行操作
    • 分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。
      • 如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。
      • 如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,
      • 如果获取到了则:
        1. this.earlySingletonObjects.put(beanName, singletonObject);
        2. this.singletonFactories.remove(beanName);
  • 三级缓存分别指: ```java /* 单例对象的缓存: bean name —> bean instance / private final Map singletonObjects = new ConcurrentHashMap(256);

/* 早期单例对象的缓存: bean name —> bean instance 提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories(三级缓存)互斥】/ private final Map earlySingletonObjects = new HashMap(16);

/* 单例工厂的缓存: bean name —> ObjectFactory / private final Map> singletonFactories = new HashMap>(16);

  1. - 怎么检测是否存在循环依赖
  2. - Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。
  3. - Spring中循环依赖场景有:<br />(1)构造器的循环依赖<br />(2field属性的循环依赖。
  4. <a name="j3N4I"></a>
  5. ## Bean实例化(装配/生命周期)过程()
  6. - 实例化Bean
  7. - 通过反射的特性生成对象
  8. - 填充Bean的属性
  9. - populateBean(), 循环依赖问题(三级缓存)
  10. - 调用aware接口相关的方法
  11. - invokeAwareMethod(完成BeanName,BeanFactory,BeanClassLoader对象的属性设置)
  12. - 调用BeanPostProcessor中的前置处理方法
  13. - (使用比较多的有ApplicationContextPostProcessor, 设置ApplicationContext, Environment, ResourceLoader, EmbeddValueResolve等对象)
  14. - 调用initmethod方法,
  15. - invokeinitmethod()判断是否实现了initializingBean接口, 如果有则调用afterPropertiesSet()方法
  16. - 调用BeanPostProcessor的后置处理方法
  17. - aop在此实现, AbstractAutoProxyCreator注册Destuction相关的回调接口
  18. - 调用BeanFactorygetBean()方法来进行对象的获取
  19. - 销毁流程
  20. - 调用DisposableBeandestroy();
  21. - 调用定制的destroy-method方法;
  22. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2480472/1647249232098-a05fb02c-678a-48cd-96e0-7331bde5c427.png#clientId=u1beb51dc-3ae6-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=372&id=jm9Ad&margin=%5Bobject%20Object%5D&name=image.png&originHeight=372&originWidth=786&originalType=binary&ratio=1&rotation=0&showTitle=false&size=79988&status=done&style=none&taskId=ude1c1c42-1dad-4aa5-99c5-8a58084a41a&title=&width=786)
  23. <a name="PZkSK"></a>
  24. # IOC
  25. IocInversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。
  26. 1. 控制反转,
  27. 1. **降低耦合**
  28. 1. 把对象的创建和对象之间的调用交给Spring管理,
  29. 1. Spring反向控制应用程序所需要使用的外部资源
  30. 2. xml解析、工厂模式、反射
  31. <a name="jI68m"></a>
  32. ## IOC(BeanFactory)底层原理
  33. 1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
  34. 1. Spring提供IOC容器实现两种方式:
  35. 1. BeanFactoryIOC容器基本实现,是Spring内部使用的接口,不提供开发人员使用 **加载配置文件时不会创建对象,只有在使用对象时才会创建对象**
  36. 1. ApplicationContextBeanFactory接口的子接口,提供更多更改强大的功能,一般由开发人员使用**加载配置文件时就会创建对象**
  37. 1. ApplicationContext实现类:
  38. - FileSystemXmlApplicationContext 文件路径加载
  39. - ClassPathXmlApplicationContext 类加载
  40. <a name="f4aa9842"></a>
  41. #### ApplicationContext 与 BeanFactory 的区别
  42. 1. BeanFactory创建的bean采用延迟加载的形式,使用时创建
  43. 1. ApplicationContextBeanFactory基础上构建,除了拥有BeanFactory的所有特性,它还支持事件发布、国际化信息的支持等。
  44. 1. ApplicationContext管理的对象会在容器初始化的时候将其全部初始化。
  45. <a name="ef4af9ff"></a>
  46. ### IOC XML
  47. **Bean管理是指两个操作**
  48. <a name="5f92bea9"></a>
  49. #### 基本标签配置
  50. ```xml
  51. <!--配置User对象创建-->
  52. <bean id="user" class="top.pastors.spring5.User"></bean>
  • id:唯一标识 不能加特殊符号
  • class:Bean的全限定名称
  • name:与id功能类似 可以起多个标识 可以使用特殊字符

scope

Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,但是具体还是要结合具体scope的Bean去研究。Spring 的 bean 作用域(scope)类型

  1. singleton:单例,默认作用域。
  2. prototype:原型,每次创建一个新对象。
  3. request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
  4. session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
  5. global-session:全局会话,所有会话共享一个实例。

线程安全这个问题,要从单例与原型Bean分别进行说明。

  • 原型Bean
    • 对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
  • 单例Bean
    • 对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
    • 单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作
    • 单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

Bean生命周期

init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称

  1. <bean id="userDao" init-method="init" destroy-method="destroy" class="top.pastors.dao.impl.UserDaoImpl"/>

Bean实例化三种方式

  1. 使用无参构造方法实例化
    1. 它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败
  2. 工厂静态方法实例化
    1. 工厂的静态方法返回Bean实例
  3. 工厂实例方法实例化
    1. 工厂的非静态方法返回Bean实例

第三方Bean的配置方式

  1. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
  2. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  3. <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
  4. <property name="username" value="root"/>
  5. <property name="password" value="root"/>
  6. </bean>

DI 依赖注入

应用程序运行依赖的资源由Spring为其提供,资源进入应用程序的方式成为注入

property set方法注入(主流)
  1. <!--1.对需要注入的变量添加set方法-->
  2. <bean id="book" class="top.pastors.spring5.Book">
  3. <!--3.将要注入的引用类型通过property属性进行注入
  4. name:要注入的变量名
  5. vlaue:属性声明标识
  6. ref:引用类型标识
  7. -->
  8. <property name="bname" vlaue="易筋经"></property>
  9. <property name="bauthor" value="达摩老祖"></property>
  10. <property name="userDao" ref="userDaoImpl"></property>
  11. </bean>
  12. <!--2.如果是引用类型要将注入的资源声明为Bean-->
  13. <bean id="userDaoImpl" class="top.pastors.spring5.UserDaoImpl" />

constructor-arg 构造方法注入
  1. <!--1.对需要注入的变量添加构造方法-->
  2. <bean id="book" class="top.pastors.spring5.Book">
  3. <!--3.将要注入的引用类型通过property属性进行注入
  4. name:参数名称 可省略 (按照顺序依次赋值)
  5. vlaue:值
  6. type:参数类型
  7. index:参数索引 从0开始(与name功能相似)
  8. -->
  9. <constructor-arg name="bname" vlaue="易筋经"/>
  10. <constructor-arg name="bauthor" value="达摩老祖"/>
  11. <constructor-arg name="userDao" ref="userDaoImpl"/>
  12. </bean>
  13. <!--2.如果是引用类型要将注入的资源声明为Bean-->
  14. <bean id="userDaoImpl" class="top.pastors.spring5.UserDaoImpl" />

p名称空间注入
  1. <beans xmlns="http://www.springframework.org/schema/beans" ...>
  2. <bean id="boot" class="top.pastors.spring5.book" p:name="九阳神功" p:author="无名氏"/>
  3. </beans>

import 外部文件导入

如果外部引入有相同id的多个bean 新值覆盖旧址 同一个配置文件中不能配置两个相同id的bean

  1. <import resource="applicationContext-user.xml"/>
  2. ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-user.xml","applicationContext-book.xml");

context 外部文件导入
  1. <!--开启context命名空间-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"... >
  3. <context:property-placeholder location="classpath:*.properties"/>
  4. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
  5. <property name="driverClassName" value="${jdbc.driver}"/>
  6. <property name="url" value="${jdbc.url}"/>
  7. <property name="username" value="${jdbc.username}"/>
  8. <property name="password" value="${jdbc.password}"/>
  9. </bean>
  10. </beans>
  11. jdbc.driver=com.mysql.jdbc.Driver
  12. jdbc.url=jdbc:mysql://localhost:3306/db1
  13. jdbc.username=root
  14. jdbc.password=root

properties 外部文件导入
  1. <!--开启context命名空间-->
  2. <beans xmlns="http://www.springframework.org/schema/beans" ...>
  3. <context:property-placeholder location="classpath:*.properties"/>
  4. <environments default="mysql">
  5. <environment id="mysql">
  6. <transactionManager type="JDBC"></transactionManager>
  7. <dataSource type="POOLED">
  8. <property name="driver" value="${jdbc.driver}"/>
  9. <property name="url" value="${jdbc.url}"/>
  10. <property name="username" value="${jdbc.username}"/>
  11. <property name="password" value="${jdbc.password}"/>
  12. </dataSource>
  13. </environment>
  14. </environments>
  15. </beans>
  16. jdbc.driver=com.mysql.jdbc.Driver
  17. jdbc.url=jdbc:mysql://localhost:3306/db1
  18. jdbc.username=root
  19. jdbc.password=root

字面量
  1. 1.null值
  2. <property name="bauthor">
  3. <null/>
  4. </property>
  5. 2. 属性值包含特殊符号
  6. a. 使用转义字符
  7. b. 把带有特殊符号的内容 <<南京>> 写到CDATA中
  8. <property name="bauthor" >
  9. <value><![CDATA[<<南京>>]]></value>
  10. </property>

注入属性

外部bean
  1. <!--beam.xml-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="userService" class="top.pastors.UserService">
  8. <!--注入UserDao对象
  9. name:类中的属性名称
  10. ref:创建userDao对象bean标签id值
  11. -->
  12. <property name="userDao" ref="userDaoImpl"></property>
  13. </bean>
  14. <bean id="userDaoImpl" class="top.pastors.UserDaoImpl"></bean>
  15. </beans>
  1. //测试
  2. @Test
  3. puglic void testBean(){
  4. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  5. Emp emp = context.getBean("emp",Emp.class);
  6. emp.add();
  7. }

内部bean
  1. <!--beam.xml-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="emp" class="top.pastors.Emp">
  8. <property name="ename" value="lucy"></property>
  9. <property name="gender" value="女"></property>
  10. <!--配置对象类型属性-->
  11. <property name="dept">
  12. <bean id="dept" class="top.pastors.Dept">
  13. <property name="dname" value="安保部"></property>
  14. </bean>
  15. </property>
  16. </bean>
  17. </beans>
  1. //测试
  2. @Test
  3. puglic void testBean(){
  4. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  5. Emp emp = context.getBean("emp",Emp.class);
  6. emp.add();
  7. }

级联赋值
  1. <!--beam.xml-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="emp" class="top.pastors.Emp">
  8. <property name="ename" value="lucy"></property>
  9. <property name="gender" value="女"></property>
  10. <!--配置对象类型属性-->
  11. <property name="dept" ref="dept"></property>
  12. <!--或者-->
  13. <property name="dept.dname" value="财务部"></property>
  14. </bean>
  15. <bean id="dept" class="top.pastors.Dept">
  16. <property name="dname" value="安保部"></property>
  17. </bean>
  18. </beans>
  1. //测试
  2. @Test
  3. puglic void testBean(){
  4. ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  5. Emp emp = context.getBean("emp",Emp.class);
  6. emp.add();
  7. }

集合属性注入
  1. <!--beam.xml-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="stu" class="top.pastors.Stu">
  8. <!--注入数组类型属性-->
  9. <property name="arrays">
  10. <array>
  11. <value>课程1</value>
  12. <value>课程2</value>
  13. </array>
  14. </property>
  15. <!--注入properties集合类型属性-->
  16. <property name="properties">
  17. <props>
  18. <prop key="name">课程1</prop>
  19. <prop key="value">6666</prop>
  20. </props>
  21. </property>
  22. <!--注入List集合类型属性-->
  23. <property name="list">
  24. <list>
  25. <value>zhangsan</value>
  26. <value>lisi</value>
  27. </list>
  28. </property>
  29. <!--注入Map集合类型属性-->
  30. <property name="maps">
  31. <map>
  32. <entry key="name" value="值1"/>
  33. <entry key="value" value="值2"/>
  34. </map>
  35. </property>
  36. <!--注入Set集合类型属性-->
  37. <property name="sets">
  38. <set>
  39. <value>值1</value>
  40. <value>值2</value>
  41. </set>
  42. </property>
  43. <!--注入对象类型属性-->
  44. <property name="lists">
  45. <list>
  46. <ref bean="course1"></ref>
  47. <ref bean="course2"></ref>
  48. </list>
  49. </property>
  50. </bean>
  51. <bean id="course1" class="top.pasotrs.Course">
  52. <property name="cname" value="值1"></property>
  53. </bean>
  54. <bean id="course2" class="top.pasotrs.Course">
  55. <property name="cname" value="值2"></property>
  56. </bean>
  57. </beans>
  1. <!--提取复用代码-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xmlns:util="http://www.springframework.org/schema/util"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/util
  9. http://www.springframework.org/schema/util/spring-util.xsd">
  10. <util:list id="bookList">
  11. <value>值1</value>
  12. <value>值2</value>
  13. <value>值3</value>
  14. </util:list>
  15. <bean id="book" class="top.pastors.Book">
  16. <property name="list" ref="bookList"></property>
  17. </bean>
  18. </beans>

SpEL SpringEL

  • 常量
    • {10} #{3.14} #{2e5} #{‘itdd’}

  • 引用bean
    • {beanld}

  • 引用bean属性
    • {beanld.propertyName}

  • 引用bean方法
    • beanld.methodName().method()
  • 引用静态方法
    • T(java.lang.Math).PI
  • 运算符支持
    • {3 lt 4 == 4 ge 3}

  • 正则表达式支持
    • {user.name matches ‘[a-z]{6,}’}

  • 集合支持
    • {like[3]}

      ```xml

  1. <a name="a4e227a3"></a>
  2. #### Spring整合Mybatis
  3. ```xml
  4. <!--spring整合mybatis后控制的创建连接用的对象-->
  5. <bean class="org.mybatis.spring.SqlSessionFactoryBean">
  6. <property name="dataSource" ref="dataSource"/>
  7. <property name="typeAliasesPackage" value="top.pastors.domain"/>
  8. </bean>
  9. <!--加载mybatis映射配置的扫描,将其作为spring的bean进行管理-->
  10. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  11. <property name="basePackage" value="top.pastors.dao"/>
  12. </bean>

IOC注解

开启注解功能

  • 无论是注解格式还是XML配置格式,最终都是将资源加载到IIOC容器中
  • 从加载效率上来说注解要优于XML
  1. <context:component-scan base-package="包名"/>

@Component

  • 类型:类注解
  • 作用:设置该类为spring管理的bean
  • 属性:定义Bean的访问ID
  • 说明:
    • @Controller()、@Service()、@Repository() 都是@Componet衍生注解 功能一致
  1. @Component("userService")
  2. public class UserServiceImpl impements UserDao{}

@Scope

  • 类型:类注解
  • 作用:定义Bean作用域
  • 属性:默认为:singleton(单例模式) 可选为:prototype(原型模式)
  • 说明:
    • 该类对应ben标签中的scope属性
  1. @Scope
  2. public class UserServiceImpl impements UserDao{}

@PostConstruct

  • 类型:方法注解
  • 作用:指定初始化方法
  • 说明:
    • 该类对应ben标签中的init-method属性
  1. @PostConstruct
  2. public void init(){}

@PreDestroy

  • 类型:方法注解
  • 作用:指定销毁方法
  • 说明:
    • 该类对应ben标签中的destroy-method属性
  1. @PreDestroy
  2. public void destroy(){}

@Bean

  • 类型:方法注解
  • 作用:设置该方法的返回值为spring管理的bean
  • 属性:定义Bean的访问ID
  • 说明:
    • 因为第三方bean无法在其源码上修改,使用@Bean解决第三方bean的引入问题
    • 该注解用于替换XML配置文件中的静态工程与实例工厂创建bean 不区分方法是否为静态或非静态
    • @Bean所在的类必须被spring扫描加载,否则无法生效

@Value

  • 类型:属性注解、方法注解
  • 作用:设置对应属性的值
  • 属性:定义对应的属性值或参数值
  • 说明:
    • value值仅支持费引用类型数据,赋值时对方法的所有参数全部赋值
    • value值支持读取properties文件中的属性值,通过类属性将properties中数据传入类中
    • value支持SpEL
    • 如果添加在属性上方,可以省略set方法
  1. @Value("${jdbc.username}")
  2. private String username;

@Autowired、@Qualifier

  • 类型:属性注解、方法注解
  • 作用:(自动装配)设置对应属性的对象或对方法进行引用类型传参
  • 属性:
    • required:定义该属性是否允许为null
  • 说明:
    • @Autowired默认按类型装配,指定@Qualifier后可以指定自定装配的bean的id
    • @Inject与@Named是JSR330规范中的注解,功能与@Autowired、@Qualifier完全相同,适用于不同架构场景
    • @Resource是JSR250规范中的注解,可以简化书写格式
      • name:设置注入的bean的id
      • type:设置注入的bean的类型,接收的参数为Class类型
  1. @Autowired(required = false)
  2. @Qualifier("userDao")
  3. private UserDao userDao;

@PropertySource

  • 类型:类注解
  • 作用:加载properties文件
  • 属性:
    • value:设置加载的properties文件名
    • ignoreResourceNotFound:如果资源未找到,是否忽略,默认为false
  • 说明:不支持 * 通配格式,一旦加载,所有的spirng控制的bean中均可使用对应属性值
  1. @PropertySource(value = "classpath:1.properties")
  2. public class Test{
  3. @Value("${num}")
  4. private int num;
  5. }

@Configuration、@ComponentScan

  • 类型:类注解
  • 作用:(替换XML)设置当前类为spring核心配置加载类
  • 说明:
    • 核心配置类用于替换spring核心配置文件,此类可以设置空的,不设置变量与属性
    • bean扫描工作使用注解@ComponentScan替代
  1. @Configuration
  2. @ComponentScan("包名")
  3. public class Test{}
  4. //Spring创建的对象换为
  5. ApplicationContext ctx = new AnnotationConfigApplicationContext(Test.class);

@Configurable

  • 类型:类注解
  • 作用:用来自动注入bean的注解,不需要通过BeanFactory去获取
  • 说明:
    1. @Configurable
    2. public class Test{}

@Import

  • 类型:类注解
  • 作用:导入第三方bean作为spring控制的资源
  • 说明:
    • 在同一类上,仅允许添加一次,如果需要导入多个,使用数组的形式进行设定
    • 在被导入的类中可以继续使用@Import导入其他资源
    • @Bean所在的类可以使用导入的形式进入spring容器,无需声明为bean
  1. @Configurable
  2. @Import(Test1.class)
  3. public class Test{}

加载控制

@DependsOn
  • 类型:类注解、方法注解
  • 作用:控制Bean的加载顺序,使其在指定bean加载完毕后再加载
  • 属性:设置当前bean所依赖bean的id
  • 说明:
    • 配置在方法上,优先于@Bean配置的bean进行加载
    • 配置在类上,优先于当前类中所有@Bean配置的bean进行加载
    • 配置在类上,优先于当@Component等配置的bean进行加载
  1. @DependsOn(“beanId”)
  2. public class Test{}

@Order
  • 类型:配置类注解
  • 作用:控制配置类的加载顺序
  • 属性:从1开始(最先加载)
  1. @Order(1)
  2. public class Test{}

@Lazy
  • 类型:类注解、方法注解
  • 作用:控制bean的加载时机,使其延迟加载
  1. @Order(1)
  2. public class Test{}

AOP

1、AOP:Aspect Oriented Programming(面向切面编程),OOP是面向对象编程,AOP是在OOP基础之上的一种更高级的设计思想。
2、OOP和AOP之间也存在一些区别,OOP侧重于对象的提取和封装。——封装对象
AOP侧重于方面组件,方面组件可以理解成封装了通用功能的组件,方面组件可以通过配置方式,灵活地切入到某一批目标对象方法上。——封装功能
3、AOP用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等。

  • 在实际开发中,比如商品查询、促销查询等业务,都需要记录日志、异常处理等操作,AOP把所有共用代码都剥离出来,单独放置到某个类中进行集中管理,在具体运行时,由容器进行动态织入这些公共代码。
  • AOP主要一般应用于签名验签、参数校验、日志记录、事务控制、权限控制、性能统计、异常处理等。

AOP概念

  1. Joinpoint 连接点
  2. Pointcut 切入点
  3. Advice 通知
    1. 通知类型
  4. Aspect 切面
  5. Target 目标对象
  6. Weaving 织入
  7. Proxy 代理
  8. Introduction 引入/引介
  • 开发阶段(开发者完成)
    • 正常的制作程序
    • 将非共性功能开发到对应的目标对象类中,并制作切入点方法
    • 将共性功能独立开发出来,制作成通知
    • 在配置文件中,声明切入点
    • 在配置文件中,声明切入点通知间的关系(含通知类型),即切面
  • 运行阶段(AOP完成)
    • Spring容器加载配置文件,监控所有配置的切入点方法的执行
    • 当监控到切入点方法被运行,使用代理机制,动态创建目标对象代理对象,根据通知类型,在代理对象的对应位置将通知对应的功能织入,完成完整代码逻辑并运行

AOP底层原理

  • Spring AOP使用的是动态代理。
    • 所谓的动态代理,就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理, 并回调原对象的方法。
  • Spring AOP中的动态代理,主要有两种方式:
    • JDK动态代理和CGLIB动态代理。JDK动态代理通过“反射”来接收被代理的类,并且要求被代理的类必须实现一个接口。
    • JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
  • CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态地生成某个类的子类。注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

AOP常用注解

AOPXML

AspectJ:Aspect切面在java中的实现

  1. <!--引用AOP命名空间-->
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <!--开启AOP命名空间-->
  10. <bean id="userService" class="top.pastors.service.impl.UserServiceImpl"/>
  11. <!--配置共性功能成供spring控制的资源-->
  12. <bean id="myAdvice" class="top.pastors.aop.AOPadvice"/>
  13. <!--开启AOP配置 可多个-->
  14. <aop:config>
  15. <!--配置公共切入点 可多个
  16. -->
  17. <aop:pointcut id="pt" expression="execution(* *..*(..))"/>
  18. <!--配置具体AOP通知对应的切入点 可多个
  19. ref:指定对应通知类Bean的ID
  20. -->
  21. <aop:aspect ref="myAdvice">
  22. <!--前切入点
  23. method:共性功能类中的方法
  24. pointcut-ref:切入点id
  25. -->
  26. <aop:before method="function" pointcut-ref="pt"/>
  27. <!--配置局部切入点 可多个
  28. expression与pointcut 功能一致 直接使用切入点表达式
  29. -->
  30. <aop:before method="function" expression="execution(* *..*(..))"/>
  31. <aop:before method="function" pointcut="execution(* *..*(..))"/>
  32. </aop:aspect>
  33. </aop:config>
  34. </beans>

切入点表达式

  • 切入点描述的是某个方法
  • 切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式
  • 关键字 (访问修饰符 返回值 包名.类名.方法名(参数) 异常名)
    • 关键字:描述表达式的匹配模式(参看关键字列表)
    • 访问修饰符:方法的访问控制权限修饰符
    • 类名:方法所在的类(此处可以配置接口名称)
    • 异常:方法定义中指定抛出的异常
  • 例如
  • execution (public User top.pastors.service.UserService.findById(int))
    • execution:匹配执行指定方法
    • args:匹配带有指定参数类型的方法
    • within:
    • this:
    • target:
    • @args:
    • @within:
    • @target:
    • @annotation:
    • bean:
    • reference pointcut:

切入点表达式通配符
  • *:单个独立的任意符号,可以独立出现,也可以作为前戳或者后戳的匹配符出现
    • execution (public * top.pastors.*.UserService.find*(*))
    • 匹配top.pastors包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
  • ..:多个联系的任意字符,可以独立出现,常用于简化包名与参数的书写
    • execution (public User top..UserService.findById(..))
    • 匹配top包下的任意包中的UserService类或接口中所有名称为findById带有任意个参数的方法
  • +:专用于匹配子类类型
    • execution (* *..*Service+.*(..))

切入点表达式逻辑运算符
  • &&:(并且)连接两个切入点表达式,表示两个切入点表达式同时成立的匹配
  • ||:(或者)连接两个切入点表达式,表示两个切入点表达式成立任意一个匹配
  • !:(非)连接两个切入点表达式,表示该切入点表达式不成立的匹配

切入点表达式范例
  1. //任意方法拦截
  2. execution(* *(..))
  3. //任意方法拦截
  4. execution(* *..*(..))
  5. //任意方法拦截
  6. execution(* *..*.*(..))
  7. //public 任意方法拦截
  8. execution(public * *..*.*(..))
  9. //public 返回值为int 任意方法拦截
  10. execution(public int *..*.*(..))
  11. //public 返回值为void 任意方法拦截
  12. execution(public void *..*.*(..))
  13. //public 返回值为void top包下的任意方法拦截
  14. execution(public void top..*.*(..))
  15. //public 返回值为void top包下的任意包下的service包下的类或接口拦截
  16. execution(public void top..service.*.*(..))
  17. //public 返回值为void top包下的pastors包下的service包下的类或接口拦截
  18. execution(public void top.pastors.service.*.*(..))
  19. //public 返回值为void top包下的pastors包下的service包下的以User开头的类或接口的任意方法拦截
  20. execution(public void top.pastors.service.User*.*(..))
  21. //public 返回值为void top包下的pastors包下的service包下的以Service结尾的类或接口的任意方法拦截
  22. execution(public void top.pastors.service.*Service.*(..))
  23. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的任意方法拦截
  24. execution(public void top.pastors.service.UserService.*(..))
  25. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的find开头的方法拦截
  26. execution(public User top.pastors.service.UserService.find*(..))
  27. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的Id结尾的方法拦截
  28. execution(public User top.pastors.service.UserService.*Id(..))
  29. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的findById的方法拦截
  30. execution(public User top.pastors.service.UserService.findById(..))
  31. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的findById下的带有1个int参数的方法拦截
  32. execution(public User top.pastors.service.UserService.findById(int))
  33. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的findById下的带有2个int参数的方法拦截
  34. execution(public User top.pastors.service.UserService.findById(int,int))
  35. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的findById下的带有1个int参数,1个任意参数的方法拦截
  36. execution(public User top.pastors.service.UserService.findById(int,*))
  37. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的findById下的带有1个任意参数,1个int参数的方法拦截
  38. execution(public User top.pastors.service.UserService.findById(*,int))
  39. //public 返回值为void top包下的pastors包下的service包下的UserService类或接口的findById下的无参数的方法拦截
  40. execution(public User top.pastors.service.UserService.findById())
  41. //public 返回值为void top包下的pastors包下的service包下的所有Service结尾的类或者接口的子类或实现类中的findAll不限参数的方法拦截
  42. execution(List top.pastors.service.*Service+.findAll(..))

切入点配置经验
  • 企业开发命名规范严格遵循规范文档进行
  • 先为方法配置局部切入点
  • 再抽取类中公共切入点
  • 最后抽取全局切入点
  • 代码走查过程中检测切入点是否存在越界性包含
  • 代码走查过程中检测切入点是否存在非包含性进驻
  • 设定AOP执行检测程序,在单元测试中监控通知被执行次数与预计次数是否匹配
  • 设定完毕的切入点如果发生调整务必进行回归测试

(以上规则适用于XML配置格式)

Spring事务

Spring事务核心对象

  • PlatfromTransactionManager:平台事务管理器
    • DataSourceTransactionManager:适用于Spring JDBC或MyBatis
    • HibernateTransactionManager:适用于Hibernate3.0及以上版本
    • JpaTransactionManager:适用于JPA
    • JdoTransactionManager:适用于JDO
    • JtaTransactionManager:适用于JTA
  • TransactionDefinition
  • TransactionStatus
    1. //获取事务
    2. TransactionStatus getTransaction(TransactionDefinition definition);
    3. //提交事务
    4. void commit(TransactionStatus status);
    5. //回滚事务
    6. void rollback(TransactionStatus status);