diff --git a/spring/1.spring基础.md b/spring/1.spring基础.md new file mode 100644 index 0000000..984fc5d --- /dev/null +++ b/spring/1.spring基础.md @@ -0,0 +1,170 @@ +  spring是为了解决企业级应用开发的复杂性而创建的,spring最根本的使命是:简化Java开发。为降低开发复杂性有以下四种关键策略。 + +- 基于POJO的轻量级和最小侵入性编程 +- 通过依赖注入和面向接口实现松耦合 +- 基于切面和惯例进行声明式编程 +- 通过切面和模板减少样板式代码 + +#### 1.依赖注入 + +​  假设类A依赖类B,通常做法是在类A中声明类B,然后使用,这样一方面具有极高的耦合性,将类A与类B绑定在一起;另一方面也让单元测试变得很困难,无法在A外部获得B的执行情况。 + +​  通过依赖注入,对象的依赖管理将不用对象本身来管理,将由一个第三方组件在创建对象时设定,依赖关系将被自动注入到对应的对象中去。 + +#### 2.创建应用上下文 + +- `ClassPathXmlApplicationContext()`从类路径创建 +- `FileSystemXmlApplicationContext()`读取文件系统下的xml配置 +- `XmlWebApplicationContext()` 读取web应用下的XML配置文件并装载上下文定义 + +#### 3.声明Bean + +1. 最简单 + + `` + +2. 带构造器 + + ```xml + + //基本数据类型使用value + //对象使用ref + + ``` + +3. 通过工厂方法创建 + + 如果想声明的Bean没有一个公开的构造函数,通过factory-method属性来装配工厂生产的Bean + + ```xml + //getInstance为获取实例的静态方法。 + ``` + +#### 4.Bean的作用域 + +​ 所有Spring Bean默认都是单例的。通过配置scope属性为prototype可每次请求产生一个新的实例。 + +```xml + +``` + +scope可选值: + +- `singleton`:每个容器中,一个Bean对象只有一个实例。(**默认**) +- `prototype`:允许实例化任意次 ,每次请求都会创建新的 +- `request`:作用域为一次http请求 +- `session`:作用域为一个http session会话 +- `global-session`:作用域为一个全局http session,仅在Protlet上下文中有效 + +#### 5.初始化和销毁Bean + +​ 当实例化需要执行初始化操作,或者销毁时需要执行清理工作。两种实现方式: + +1. xml配置,类中编写初始化方法和销毁方法,在bean中定义。 + + ```xml + + ``` + + 也可在Beans中定义默认初始化和销毁方法。 + + ```xml + + ``` + +2. 实现`InitializingBean `和`DisposableBean`接口 + +#### 6.setter注入 + +​ 在bean中使用``元素配置属性,使用方法类似于`` + +```xml + //注入基本数据类型 + //注入类 +``` + + 可使用p简写,**-ref**后缀说明装配的是一个引用 + +```xml + +``` + +#### 7.注入内部Bean + +​ 既定义其他Bean内部的Bean,避免共享问题,可在属性节点或者构造器参数节点上使用。 + +```xml + + //没有id属性,因为不会被其他bean使用 + + + + +``` + +#### 8.装配集合 + +| 集合元素 | 用途 | +| ---------------- | ------------------------------ | +| \ | 装配list类型,允许重复 | +| \ | set,不能重复 | +| \ | map类型 | +| \ | properties类型,键值都为String | + +- list + + ```xml + + + + + + + + 用来定义上下文中的其他引用,还可使用,, + ``` + +- set + + ```xml + + + + ``` + + 用法和list相同,只是不能重复 + +- Map + + ```XML + + + + ``` + + entry元素由一个key,一个value组成,分别有两种形式。 + + | key | 键为String | + | :-------- | -------------- | + | key-ref | 键为Bean的引用 | + | value | 值为String | + | value-ref | 值为Bean的引用 | + +- props + + ```xml + + guitar + + ``` + + 键值都是String + +#### 9.装配空值 + +```xml + +``` + diff --git a/spring/2.最小化XML配置.md b/spring/2.最小化XML配置.md new file mode 100644 index 0000000..c530b89 --- /dev/null +++ b/spring/2.最小化XML配置.md @@ -0,0 +1,223 @@ +## 一、自动装配 + +### 1、四种类型的自动装配 + +| 类型 | 解释 | xml配置 | +| ---------- | ------------------------------------ | ---------------------------------------------- | +| byName | 根据Bean的name或者id | \ | +| ByType | 根据Bean类型自动装配 | \ | +| contructor | 根据Bean的构造器入参具有相同类型 | 同上 | +| Autodetect | 首先使用contructor,失败再尝试byType | 同上 | + +  byType在出现多个匹配项时不会自动选择一个然是报错,为避免报错,有两种办法:1.使用\元素的primary属性,设置为首选Bean,但所有bean的默认primary都是true,因此我们需要将所有非首选Bean设置为false;2.将Bean的`autowire-candidate`熟悉设置为**false **,取消 这个Bean的候选资格,这个Bean便不会自动注入了。 + +  contructor自动装配和byType有一样的局限性,当发现多个Bean匹配某个构造器入参时,Spring不会尝试选择其中一个;此外,如果一个类有多个构造器都满足自动装配的条件,Spring也不会猜测哪个更合适使用。 + +###2、默认自动装配 + +  如果需要为Spring应用上下文中的每个Bean(或者其中的大多数)配置相同的autowire属性,可以在根元素\上增加一个default-autowire属性,默认该属性设置为none。该属性只应用于指定配置文件中的所有Bean,并不是Spring上下文中的所有Bean。 + +###3、混合使用自动装配和显式装配 + +   当我们对某个Bean使用了自动装配策略,并不代表我们不能对该Bean的某些属性进行显示装配,任然可以为任意一个属性配置\元素,显式装配将会覆盖自动装配。**但是**当使用constructor自动装配策略时,我们必须让Spring自动装配构造器所有入参,不能使用\元素进行混合。 + +## 二、注解装配 + +  从Spring2.5开始,可以使用注解自动装配Bean的属性,使用注解允许更细粒度的自动装配,可选择性的标注某一个属性来对其应用自动装配。Spring容器默认禁用注解装配,需要在Spring配置中启用,最简单的启用方式是使用Spring的context命令空间配置中的``,如下所示: + +```xml + + + + +``` + +  Spring3支持几种不同的用于自动装配的注解: + +- Spring自带的@Autowired注解 +- JSR-330的@Inject注解 +- JSR-250的@Resource注解 + +###1、使用@Autowired + +  @Autowired用于对被注解对象启动ByType的自动装配,可用于以下对象: + +- 类属性,即使私有属性也能注入 +- set方法 +- 构造器 +- 任意需要装配Bean的方法 + +在使用@Autowired时有两种情况会出错:没有匹配的Bean和存在多个匹配的Bean,但是都有对应的解决方法。 + +- 当没有匹配Bean时,自动装配会抛出NoSuchBeanDefinitionException,如果不想抛出可使用required属性,设置为false来配置可选的自动装配,即装配失败就不进行装配,不会报错。 + + ```java + @Autowired(required=false) + ``` + + 当使用构造器配置时,只有一个构造器可以将required属性设置为true,其他都只能设置为false。此外,当使用注解标注多个构造器时,Spring会从所有满足装配条件的构造器中选择入参最多的那个。 + +- 当存在多个Bean满足装配条件时,Spring也会抛出NoSuchBeanDefinitionException错误,为了选择指定的Bean,我们可以使用@Qualifier注解进行筛选: + + ```java + @Autowired + @Qualifier("name1")//筛选名为name1的Bean + private TestClass testClass; + ``` + + 除了通过Bean的ID来缩小选择范围,我们还可以通过直接在Bean上使用qualifier来缩小范围,限制Bean的类型,xml如下: + + ```xml + + + + ``` + + 注解如下: + + ```java + @Qualifier("stringed") + public class xxx{} + ``` + + 还可以创建**自定义限定器(Qualifier)** + +   创建自定义限定器只需要使用@Qualifier注解作为它的源注解即可,如下创建了一个Stringed限定器: + + ```java + @Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @Qualifier + public @interface Stringed{} + ``` + + 然后使用它注解一个Bean: + + ```java + @Stringed + public class Guitar{} + ``` + + 然后就可以进行限定了: + + ```java + @Autowired + @Stringed + private Guitar guitar; + ``` + +### 2、使用@Inject自动注入 + +  为统一各种依赖注入框架的编程模型,JCP(Java Community Process)发布的Java依赖注入规范,被称为JSR-330,从Spring3开始,Spring已经开始兼容该依赖注入模型。 + +  和@Autowired一样,@Inject可以用来自动装配属性、方法和构造器。但是@Inject没有required属性,因此依赖关系必须存在,如不存在将抛出异常。 + +  JSR-330还提供另一种注入技巧,注入一个Provider。Provider接口可以实现Bean引用的延迟注入以及注入Bean的多个实例等功能。 + +  例如我们有一个KnifeJuggler类需要注入一个或多个Knife实例,假设Knife Bean的作用域声明为prototype,下面的KnifeJuggler的构造器将获得多个Knife Bean: + +```java +private Set knifes; + +@Inject +public KnifeJuggler(Provider knifeProvider){ + knives = new HashSet(); + for(int i=0;i<5;i++){ + knives.add(knifeProvider.get()); + } +} +``` + +  相对于@Autowired所对应的@Qualifier,@Inject对应的是@Named注解。事实上JSR-330中也有@Qualifier注解,不过不建议直接使用,建议通过该注解来创建自定义的限定注解,和Spring的@Qualifier创建过程类似。 + +### 3、注解中使用表达式 + +  Spring3中引入的`@Value`属性可用来装配String类型的值和基本类型的值。借助SpEL表达式,@Value不光可以装配硬编码值还可以在运行期动态计算表达式并装配,例如下面的: + +```java +@Value("#{systemProperties.name}") +private String name; +``` + +## 三、自动检测Bean + +  在Spring中使用上面说到的``,可以做到自动装配,但还是要在xml中申明Bean。Spring还有另一个元素``,元素除了完成自动装配的功能,还允许Spring自动检测Bean和定义Bean ,用法如下: + +```xml + + + + +``` + +开启后支持如下注解: + +| 注解 | 解释 | +| ----------- | ------------------------------------ | +| @Component | 通用的构造型注解,标识类为Spring组件 | +| @Controller | 标识该类定义为Spring MVC controller | +| @Repository | 标识该类定义为数据仓库 | +| @Service | 标识该类定义为服务 | + +  使用上述注解是Bean的ID默认为无限定类名。使用`@Component("name")`指定ID。 + +### 1、过滤组建扫描 + +  通过为配置子元素,我们可以随意调整扫描行为。下面的配置自动注册所有的TestInterface实现类: + +```xml + + + +``` + +其中的type和expression属性一起协作来定义组件扫描策略。type有以下值可选择: + +| 过滤器类型 | 描述 | +| ---------- | ------------------------------------------------------------ | +| annotation | 过滤器扫描使用指定注解所标注的类。通过expression属性指定要扫描的注解 | +| assignable | 过滤器扫描派生于expression属性所指定类型的那些类 | +| aspectj | 过滤器扫描于expression属性所指定的AspectJ表达式所匹配的那些类 | +| custom | 使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定 | +| regex | 过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的类 | + +  exclude-filter使用和include-filter类似,只是效果相反。 + +## 四、使用Spring基于Java的配置 + +  在Spring3.0中几乎可以不使用XML而使用纯粹的Java代码来配置Spring应用。 + +- 首先还是需要极少量的XML来启用Java配置,就是上面说到的``,该标签还会自动加载使用`@Configuration`注解所标识的类 + +- @Configuration注解相当于XML配置中的\元素,这个注解将会告知Spring:这个类包含一个或多个Spring Bean的定义,这些定义是使用@Bean注解所标注的方法 + +- 申明一个简单的Bean代码如下: + + ```java + @Configuration + public class TestConfig{ + @Bean + public Animal duck(){ + return new Ducker(); + } + } + ``` + + @Bean告知Spring这个方法将返回一个对象,该对象应该被注册为Spring应用上下文中的一个Bean,方法名作为该Bean的ID 。想要使用另一个Bean的引用也很简单,如下: + + ```java + @Bean + public Food duckFood(){ + return new DuckFood(); + } + + @Bean //通过方法名引用一个Bean,并不会创建一个新的实例 + public Animal duck(){ + return new Ducker(DuckFood()); + } + ``` + + ## 五、小结 + +   终于写完了spring 的最小化配置,对spring的各种注解也有了一些了解,再不是之前看到注解一脸莫名其妙了,虽然现在Springboot已经帮我们做了零XML配置,但觉得还是有必要了解下XML配置实现,这样对Java的配置实现理解也会更加深刻。 \ No newline at end of file diff --git a/spring/3.面向切面的Spring.md b/spring/3.面向切面的Spring.md new file mode 100644 index 0000000..999d31a --- /dev/null +++ b/spring/3.面向切面的Spring.md @@ -0,0 +1,239 @@ + ## 一.面向切面编程 + +  Spring的基础是IOC和AOP,前面两节对IOC和DI做了简单总结,这里再对AOP进行一个学习总结,Spring基础就算有一个初步了解了。 + +  在软件开发中,我们可能需要一些跟业务无关但是又必须做的东西,比如日志,事务等,这些分布于应用中多处的功能被称为横切关注点,通常横切关注点从概念上是与应用的业务逻辑相分离的。如何将这些横切关注点与业务逻辑在代码层面进行分离,是面向切面编程(**AOP**)所要解决的。 + +​ 横切关注点可以被描述为影响应用多处的功能,切面能够帮助我们模块化横切关注点。下图直观呈现了横切关注点的概念: + +![横切关注点](./3.面向切面的Spring/切面示例.png) + +途中CourseService,StudentService,MiscService都需要类似安全、事务这样的辅助功能,这些辅助功能就被称为横切关注点。 + +  **继承**和**委托**是最常见的实现重用通用功能的面向对象技术。但是如果在整个程序中使用相同的基类继承往往会导致一个脆弱的对象体系;而使用委托可能需要对委托对象进行复杂的调用。 + +​ 切面提供了取代继承和委托的另一种选择,而且更加清晰简洁。在面向切面编程时,我们任然在一个地方定义通用功能,但是我们可以通过声明的方式定义这个功能以何种方式在何处应用,而无需修改受影响的类,受影响类完全感受不到切面的存在。 + +## 二.AOP常用术语 + +  下面是AOP中常用的名词。 + +### 1. 通知(Advice) + +  通知定义了切面是什么以及何时使用。出了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。Sping切面可以应用以下5种类型的通知。 + +- **Before** 在方法被调用之前调用通知 +- **After** 在方法完成之后调用通知,无论方法执行是否成功 +- **After-returning** 在方法成功执行后调用通知 +- **After-throwing** 在方法抛出异常后调用通知 +- **Around** 通知包裹了被通知的方法,在被通知的方法调用前和调用后执行 + +###2.连接点(Joinpoint) + +  应用可能有很多个时机应用通知,这些时机被称为连接点。连接点是应用在执行过程中能够插入切面的一个点,这个点可以是调用方法时、抛出异常时、甚至是修改字段时。切面代码可以利用这些切入到应用的正常流程中,并添加新的行为。 + +### 3.切点(Pointcut) + +  切点定义了通知所要织入的一个或多个连接点。如果说通知定义了切面的“**什么**”和“**何时**”,那么切点就定义了“**何处**”。通常使用明确的类和方法名称来指定切点,或者利用正则表达式定义匹配的类和方法来指定这些切点。有些AOP框架允许我们创建动态的切点,可以更具运行时的策略来决定是否应用通知。 + +### 4.切面(Aspect) + +  切面是通知和切点的结合。通知和切点定义了关于切面的全部内容,**是什么**,在**何时**、**何处**完成其功能。 + +### 5.引入 + +  引入允许我们想现有的类添加新方法或属性。即在无需修改现有类的情况下让它们具有新的行为和状态。 + +### 6.织入 + +  织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入。 + +- 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译期,比如AspectJ的织入编译期 +- 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的加载器,它可以在目标类被引入应用之前增强该目标类的字节码,例如AspectJ5的**LTW**(load-time weaving) +- 运行期:切面在应用运行的某个时刻被织入。一般情况下AOP容器会为目标对象动态创建一个代理对象 + +##三.Spring AOP + +  Spring在运行期通知对象,通过在代理类中包裹切面,Spring在运行期将切面织入到Spring管理的Bean中。代理类封装了目标类,并拦截被通知的方法的调用,再将调用转发给真正的目标Bean。由于Spring是基于动态代理,所有Spring只支持方法连接点,如果需要方法拦截之外的连接点拦截,我们可以利用Aspect来协助SpringAOP。 + +  Spring在运行期通知对象,通过在代理类中包裹切面,Spring在运行期将切面织入到Spring管理的Bean中。代理类封装了目标类,并拦截被通知的方法的调用,再将调用转发给真正的目标Bean。由于Spring是基于动态代理,所有Spring只支持方法连接点,如果需要方法拦截之外的连接点拦截,我们可以利用Aspect来协助SpringAOP。 + +### 1、定义切点 + +  在SpringAOP中,需要使用AspectJ的切点表达式语言来定义切点。Spring只支持AspectJ的部分切点指示器,如下表所示: + +| AspectJ指示器 | 描述 | +| ------------- | ------------------------------------------------------------ | +| arg() | 限制连接点匹配参数为指定类型的执行方法 | +| @args() | 限制连接点匹配参数由指定注解标注的执行方法 | +| execution() | 用于匹配是连接点的执行方法 | +| this() | 限制连接点匹配AOP代理的Bean引用为指导类型的类 | +| target() | 限制连接点匹配目标对象为指定类型的类 | +| @target() | 限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解 | +| within() | 限制连接点匹配指定的类型 | +| @within() | 限制连接点匹配指定注解所标注的类型(当使用SpringAOP时,方法定义在由指定的注解所标注的类里) | +| @annotation | 限制匹配带有指定注解连接点 | +| bean() | 使用Bean ID或Bean名称作为参数来限制切点只匹配特定的Bean | + + 其中只有execution指示器是唯一的执行匹配,其他都是限制匹配。因此execution指示器是 + +其中只有execution指示器是唯一的执行匹配,其他都是限制匹配。因此execution指示器是我们在编写切点定义时最主要使用的指示器。 + +### 2、编写切点 + +  假设我们要使用execution()指示器选择Hello类的sayHello()方法,表达式如下: + +```java +execution(* com.test.Hello.sayHello(..)) +``` + +方法表达式以*** **号开始,说明不管方法返回值的类型。然后指定全限定类名和方法名。对于方法参数列表,我们使用(**)标识切点选择任意的sayHello()方法,无论方法入参是什么。 + +  同时我们可以使用&&(and),||(or),!(not)来连接指示器,如下所示: + +```java +execution(* com.test.Hello.sayHello(..)) and !bean(xiaobu) +``` + +### 3、申明切面 + +  在经典Spring AOP中使用ProxyFactoryBean非常复杂,因此提供了申明式切面的选择,在Spring的AOP配置命名空间中有如下配置元素: + +| AOP配置元素 | 描述 | +| ------------------------------ | ----------------------------------------------------------- | +| <aop:advisor > | 定义AOP通知器 | +| <aop:after > | 定义AOP后置通知(无论被通知方法是否执行成功) | +| <aop:after-returning > | 定义AOP after-returning通知 | +| <aop:after-throwing > | 定义after-throwing | +| <aop:around > | 定义AOP环绕通知 | +| <aop:aspect > | 定义切面 | +| <aop:aspectj-autoproxy > | 启用@AspectJ注解驱动的切面 | +| <aop:before > | 定义AOP前置通知 | +| <aop:config > | 顶层的AOP配置元素。大多数的<aop:* >元素必须包含在其中 | +| <aop:declare-parents > | 为被通知的对象引入额外的接口,并透明的实现 | +| <aop:pointcut > | 定义切点 | + +### 4、实现 + +假设有一个演员类`Actor`,演员类中有一个表演方法`perform()`,然后还有一个观众类`Audience`,这两个类都在包`com.example.springtest`下,Audience类主要方法如下: + +```java +public class Audience{ + //搬凳子 + public void takeSeats(){} + //欢呼 + public void applaud(){} + //计时,环绕通知需要一个ProceedingJoinPoint参数 + public void timing(ProceedingJoinPoint joinPoint){ + joinPoint.proceed(); + } + //演砸了 + public void demandRefund(){} + //测试带参数 + public void dealString(String word){} + +} +``` + +#### a、xml配置实现 + +  首先将Audience配置到springIOC中: + +```xml + +``` + +然后申明通知: + +```xml + + + + + + + + + + + + + + + + + + +``` + + + +#### b、注解实现 + +直接在Audience类上加注解(Aspect注解并不能被spring自动发现并注册,要么写到xml中,要么使用@Aspectj注解或者加一个@Component注解),如下所示: + +```java +@Aspect +public class Audience{ + //定义切点 + @Pointcut(execution(* com.example.springtest.Performer.perform(..))) + public void perform(){} + + //定义带参数切点 + @Pointcut(execution(* com.example.springtest.Performer.performArg(String) and args(word))) + public void performStr(String word){} + + //搬凳子 + @Before("perform()") + public void takeSeats(){} + + //欢呼 + @AfterReturning("perform()") + public void applaud(){} + + //计时,环绕通知需要一个ProceedingJoinPoint参数 + @Around("perform()") + public void timing(ProceedingJoinPoint joinPoint){ + joinPoint.proceed(); + } + + //演砸了 + @AfterThrowing("perform()") + public void demandRefund(){} + + //带参数 + @Before("performStr(word)") + public void dealString(String word){} +} +``` + +#### c、通过切面引入新功能 + +  既然可以用AOP为对象拥有的方法添加新功能,那为什么不能为对象增加新的方法呢?利用被称为**引入**的AOP概念,切面可以为Spring Bean添加新的方法,示例图如下: + +![引入](.\3.面向切面的Spring\引入新功能.png) + +当引入接口的方法被调用时,代理将此调用委托给实现了新接口的某个其他对象。实际上,Bean的实现被拆分到了多个类。 + +- xml引入需要使用<aop:declare-parents >元素: + + ```xml + + + + ``` + + 顾名思义\<declare-parents>声明了此切面所通知的Bean在它的对象层次结构中有了新的父类型。其中types-matching指定增强的类;implement-interface指定实现新方法的接口;default-imple指定实现了implement-interface接口的实现类,也可以用delegate-ref来指定一个Bean的引用。 + +- 注解引入,通过`@DeclareParents`注解 + + ```xml + @DeclareParents(value="com.fxb.springtest.Performer+", + defaultImpl=AddTestImpl.class) + public static AddTestInterface addTestInterface; + ``` + + 同xml实现一样,注解也由三部分组成:1、value属性相当于tpes-matching属性,标识被增强的类;2、defaultImpl等同于default-imple,指定接口的实现类;3、有@DeclareParents注解所标注的static属性指定了将被引入的接口。 + diff --git a/spring/3.面向切面的Spring/切面示例.png b/spring/3.面向切面的Spring/切面示例.png new file mode 100644 index 0000000..81f8abf Binary files /dev/null and b/spring/3.面向切面的Spring/切面示例.png differ diff --git a/spring/3.面向切面的Spring/引入新功能.png b/spring/3.面向切面的Spring/引入新功能.png new file mode 100644 index 0000000..edc1877 Binary files /dev/null and b/spring/3.面向切面的Spring/引入新功能.png differ