spring基础

Spring概念及特点

Spring特点是什么?

轻量级:Spring 是非侵入性的 即基于 Spring 开发的应用中的对象可以不依赖于 Spring 的 API,而可以直接使用。
依赖注入(DI — dependency injection、IOC)
面向切面编程(AOP — aspect oriented programming)
容器: Spring 是一个容器, 因为它包含并且管理应用对象的生命周期
框架: Spring 实现了使用简单的组件配置组合成一个复杂的应用. 在 Spring 中可以使用 XML 和 Java 注解组合这些对象
一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库 (实际上 Spring 自身也提供了展现层的 SpringMVC 和 持久层的 Spring JDBC)
在这里插入图片描述

在配置Spring时,注释是否比XML更好?**
基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”。简短的答案取决于情况。长远的答案是每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于定义方式的不同,注释在声明中提供了很多上下文,从而使配置更短,更简洁。但是,XML擅长连接组件而不接触其源代码或重新编译它们。一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注释的类不再是POJO,而且,该配置变得分散且难以控制。
无论选择如何,Spring都可以容纳两种样式,甚至可以将它们混合在一起

Spring注意事项

Spring的日志输出是依赖于commons-logging,但是spring的jar包中没有给我们提供,所以我们需要自己进行下载。

Spring最主要的核心包是4个【 beans、context、core、expression】,一个简单的spring需要着5个jar包即可,如果要扩展功能的话,我们需要在源码的libs中再重新引入所需要的jar包。

Spring在连接数据库时,需要引入连接数据库的jar包,这个也需要我们另外下载

Spring的核心配置文件为【ApplicationContext.xml】,对于一些属性的配置例如基于注解时配置,或者是连接数据库配置,或者是bean配置,事务配置等等。

在配置Spring时,注释是否比XML更好?
基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”。简短的答案取决于情况。长远的答案是每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于定义方式的不同,注释在声明中提供了很多上下文,从而使配置更短,更简洁。但是,XML擅长连接组件而不接触其源代码或重新编译它们。一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注释的类不再是POJO,而且,该配置变得分散且难以控制。
无论选择如何,Spring都可以容纳两种样式,甚至可以将它们混合在一起

泛型依赖注入:
配置两个父类:其中一个父类中注入另外一个类。使得两个类产生引用关系
之后两个子类分别继承后会自动产生引用关系而不用注入[在这里插入图片描述[在这里插入图片描述

IOC

官方解释:
IoC也称为依赖注入(DI)。这是一个过程,在此过程中,对象仅通过构造函数参数,工厂方法的参数或在对象实例从工厂方法构造或返回后设置的属性来定义其依赖关系,即与它们一起使用的其他对象。然后,容器 在创建bean时注入那些依赖项。此过程从根本上来说是相反的,因此名称为Inversion of Control(IoC),即bean本身通过使用类的直接构造来控制其依赖关系的实例化或位置,或者使用诸如服务定位器模式

• IoC(Inversion of Control) 控制反转,即“不用打电话过来,我们会打给你”。
两种实现: 依赖查找(DL)和依赖注入(DI)。
IOC 和 DI 、DL 的关系(这个 DL,Avalon 和 EJB 就是使用的这种方式实现的 IoC):
在这里插入图片描述

• DL 已经被抛弃,因为他需要用户自己去是使用 API 进行查找资源和组装对象。即有侵入性。
• DI 是 Spring 使用的方式,容器负责组件的装配。
Spring 的 IoC 设计支持以下功能:
• 依赖注入
• 依赖检查
• 自动装配
• 支持集合
• 指定初始化方法和销毁方法
• 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)
其中,最重要的就是依赖注入,从 XML 的配置上说, 即 ref 标签。对应 Spring RuntimeBeanReference 对象。
对于 IoC 来说,最重要的就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。
那么, Spring 如何设计容器的呢?
Spring 作者 Rod Johnson 设计了两个接口用以表示容器。
• BeanFactory
• ApplicationContext
BeanFactory 粗暴简单,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。
ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。
当然,除了这两个大接口,还有其他的辅助接口,但我今天不会花太多篇幅介绍他们。
为了更直观的展示 “低级容器” 和 “高级容器” 的关系,我这里通过常用的 ClassPathXmlApplicationContext 类,来展示整个容器的层级 UML 关系。
在这里插入图片描述

有点复杂? 先不要慌,我来解释一下。
最上面的 BeanFactory 知道吧?我就不讲了。
下面的 3 个绿色的,都是功能扩展接口,这里就不展开讲。
看下面的隶属 ApplicationContext 粉红色的 “高级容器”,依赖着 “低级容器”,这里说的是依赖,不是继承哦。他依赖着 “低级容器” 的 getBean 功能。而高级容器有更多的功能:支持不同的信息源头,可以访问文件资源,支持应用事件(Observer 模式)。
通常用户看到的就是 “高级容器”。 但 BeanFactory 也非常够用啦!
左边灰色区域的是 “低级容器”, 只负载加载 Bean,获取 Bean。容器其他的高级功能是没有的。例如上图画的 refresh 刷新 Bean 工厂所有配置。生命周期事件回调等。
好,解释了低级容器和高级容器,我们可以看看一个 IoC 启动过程是什么样子的。说白了,就是 ClassPathXmlApplicationContext 这个类,在启动时,都做了啥。(由于我这是 interface21 的代码,肯定和你的 Spring 4.x 系列不同)。
下图是 ClassPathXmlApplicationContext 的构造过程,实际就是 Spring IoC 的初始化过程。
在这里插入图片描述

注意,这里为了理解方便,有所简化。
这里再用文字来描述这个过程:
1. 用户构造 ClassPathXmlApplicationContext(简称 CPAC)
2. CPAC 首先访问了 “抽象高级容器” 的 final 的 refresh 方法,这个方法是模板方法。所以要回调子类(低级容器)的 refreshBeanFactory 方法,这个方法的作用是使用低级容器加载所有 BeanDefinition 和 Properties 到容器中。
3. 低级容器加载成功后,高级容器开始处理一些回调,例如 Bean 后置处理器。回调 setBeanFactory 方法。或者注册监听器等,发布事件,实例化单例 Bean 等等功能,这些功能,随着 Spring 的不断升级,功能越来越多,很多人在这里迷失了方向 :)。
简单说就是:
1. 低级容器 加载配置文件(从 XML,数据库,Applet),并解析成 BeanDefinition 到低级容器中。
2. 加载成功后,高级容器启动高级功能,例如接口回调,监听器,自动实例化单例,发布事件等等功能。
所以,一定要把 “低级容器” 和“高级容器” 的区别弄清楚。不能一叶障目不见泰山。
好,当我们创建好容器,就会使用 getBean 方法,获取 Bean,而 getBean 的流程如下:在这里插入图片描述

从图中可以看出,getBean 的操作都是在低级容器里操作的。其中有个递归操作,这个是什么意思呢?
假设 : 当 Bean_A 依赖着 Bean_B,而这个 Bean_A 在加载的时候,其配置的 ref = “Bean_B” 在解析的时候只是一个占位符,被放入了 Bean_A 的属性集合中,当调用 getBean 时,需要真正 Bean_B 注入到 Bean_A 内部时,就需要从容器中获取这个 Bean_B,因此产生了递归。
为什么不是在加载的时候,就直接注入呢?因为加载的顺序不同,很可能 Bean_A 依赖的 Bean_B 还没有加载好,也就无法从容器中获取,你不能要求用户把 Bean 的加载顺序排列好,这是不人道的。
所以,Spring 将其分为了 2 个步骤:
1. 加载所有的 Bean 配置成 BeanDefinition 到容器中,如果 Bean 有依赖关系,则使用占位符暂时代替。
2. 然后,在调用 getBean 的时候,进行真正的依赖注入,即如果碰到了属性是 ref 的(占位符),那么就从容器里获取这个 Bean,然后注入到实例中 —— 称之为依赖注入。
可以看到,依赖注入实际上,只需要 “低级容器” 就可以实现。

AOP

AOP(Aspect-Oriented Programming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用拦截方法的方式,对该方法进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“切面”,从而使得编译器可以在编译期间织入有关“切面”的代码。
AOP术语

为了理解AOP,我们必须先了解AOP的相关术语:

切面(Aspect)

横切关注点的模块化(跨越应用程序多个模块的功能,比如 日志功能),这个关注点实现可能另外横切多个对象。

通知(Advice)

在AOP中,描述切面要完成的工作被称为通知。
Spring AOP 可以应用5种类型的通知:

前置通知(Before):在目标方法被调用之前调用通知功能
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

目标(Target)

包含连接点的对象。也被称作被通知或被代理对象。

代理(Proxy)

向目标对象应用通知之后创建的对象。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

连接点(Join point)

连接点是在应用执行过程中能够插入切面的一个点。这个点可以是类的某个方法调用前、调用后、方法抛出异常后等。切面代码可以利用这些点插入到应用的正常流程之中,并添加行为。

切点(Pointcut)

指定一个通知将被引发的一系列连接点的集合。AOP 通过切点定位到特定的连接点。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。每个类都拥有多个连接点,例如 ArithmethicCalculator类的所有方法实际上都是连接点。

引入(Introduction)

引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。

织入(Weaving)

织入描述的是把切面应用到目标对象来创建新的代理对象的过程。
Spring AOP 的切面是在运行时被织入,原理是使用了动态代理技术。Spring支持两种方式生成代理对象:JDK动态代理和CGLib,默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。
Spring AOP使用

在 Spring2.0 以上版本中, 可以使用基于AspectJ 注解或基于 XML 配置的 AOP。
基于注解的方式

1、在pom.xml中声明AOP依赖。

< properties >
         < org.springframework.version >4.2.4.RELEASE </ org.springframework.version >
         < aspectj.version >1.8.8 </ aspectj.version >
         < java.version >1.8 </ java.version >
         < maven.compiler.version >3.1 </ maven.compiler.version >
         < junit.version >4.12 </ junit.version >
         < project.build.sourceEncoding >UTF-8 </ project.build.sourceEncoding >
    </ properties >
    <!-- AOP dependency -->
         < dependency >
             < groupId >org.springframework </ groupId >
             < artifactId >spring-aop </ artifactId >
             < version >${org.springframework.version} </ version >
         </ dependency >
 
         < dependency >
             < groupId >org.aspectj </ groupId >
             < artifactId >aspectjrt </ artifactId >
             < version >${aspectj.version} </ version >
         </ dependency >
         < dependency >
             < groupId >org.aspectj </ groupId >
             < artifactId >aspectjweaver </ artifactId >
             < version >${aspectj.version} </ version >
             < scope >runtime </ scope >
         </ dependency >  

2、以用户注册、登录为例,UserService负责处理用户注册、登录等相关逻辑。

package  com.ricky .codelab.spring.aop; 
 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
import  com.ricky.codelab.spring.aop.service.UserService; 
 
public class SpringAOPMain {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml" ) ;
        UserService userService = (UserService) ctx .getBean ( "userService" ) ; 
        userService .login ( "ricky" ,  "123" ) ;
        userService .register ( "jack" ,  "abc" ) ;
        ctx .close () ; 
    }
 
}   

UserService 接口如下:

package com.ricky.codelab.spring.aop.service;
 
public interface  UserService {
 
     public  String  login (String username, String passowrd);
     public  String  register (String username, String passowrd);
}

UserService 实现类代码:

package  com.ricky.codelab.spring.aop.service;
 
 import  org.springframework.stereotype.Service;
 
 @Service ( "userService" )
 public    class   UserServiceImpl   implements   UserService  { 
 
     @Override 
     public  String  login (String username, String passowrd) {
        System.out.println( "login username:" +username+ ",passowrd:" +passowrd);
         return   "OK" ;
    }
 
     @Override 
     public  String  register (String username, String passowrd) {
        System.out.println( "register username:" +username+ ",passowrd:" +passowrd);
         return   "OK" ;
    }
 
}

3、假设我们需要在UserService 的login方法执行前后增加日志服务模块,接下来我们要定义个切面,也就是所谓的日志功能的类。

package  com.ricky.codelab.spring.aop;
 
 import  java.util.Arrays;
 import  java.util.List;
 
 import  org.aspectj.lang.JoinPoint;
 import  org.aspectj.lang.ProceedingJoinPoint;
 import  org.aspectj.lang.annotation.After;
 import  org.aspectj.lang.annotation.AfterReturning;
 import  org.aspectj.lang.annotation.AfterThrowing;
 import  org.aspectj.lang.annotation.Around;
 import  org.aspectj.lang.annotation.Aspect;
 import  org.aspectj.lang.annotation.Before;
 import  org.springframework.stereotype.Component;
 
 @Aspect 
 @Component 
 public    class   LogAspect  { 
 
     /**
     * 前置通知:在方法执行前执行的代码
     *  @param  joinpoint
     */ 
     @Before ( "execution(String com.ricky.codelab.spring.aop.service.UserService.login(String,String))" )
     public   void   beforeExecute (JoinPoint joinPoint){
 
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
 
        System.out.println( "The method " +methodName+  " begin with " +args);
    }
 
     /**
     * 后置通知:在方法执行后执行的代码(无论该方法是否发生异常),注意后置通知拿不到执行的结果
     *  @param  joinpoint
     */ 
     @After ( "execution(String com.ricky.codelab.spring.aop.service.UserService.login(String,String))" )
     public   void   afterExecute (JoinPoint joinPoint){
 
        String methodName = joinPoint.getSignature().getName();
        System.out.println( "The method " +methodName+ " end!" );
    }
 
     /**
     * 返回通知:在方法正常执行后执行的代码,可以获取到方法的返回值
     *  @param  joinpoint
     */ 
     @AfterReturning (value= "execution(String com.ricky.codelab.spring.aop.service.UserService.login(String,String))" ,
            returning= "result" )
     public   void   afterReturning (JoinPoint joinPoint, Object result){
 
        String methodName = joinPoint.getSignature().getName();
        System.out.println( "The method " +methodName+ " end with result:" +result);
    }
 
     /**
     * 异常通知:在方法抛出异常之后执行,可以访问到异常信息,且可以指定出现特定异常信息时执行代码
     *  @param  joinpoint
     */ 
     @AfterThrowing (value= "execution(String com.ricky.codelab.spring.aop.service.UserService.login(String,String))" ,
            throwing= "exception" )
     public   void   afterThrowing (JoinPoint joinPoint, Exception /**NullPointerException*/ exception){
 
        String methodName = joinPoint.getSignature().getName();
        System.out.println( "The method " +methodName+ " occurs exception:" +exception);
    }
 
     /**
     * 环绕通知, 围绕着方法执行
     */ 
     @Around ( "execution(String com.ricky.codelab.spring.aop.service.UserService.login(String,String))" )
     public  Object  aroundMethod (ProceedingJoinPoint joinPoint){
 
        String methodName = joinPoint.getSignature().getName();
        System.out.println( "The method " +methodName+ " around" );
 
        Object result =  null ;
         try  {
            result = joinPoint.proceed();
        }  catch  (Throwable e) {
            e.printStackTrace();
        }
 
         return  result;
    }
}

在LogAspect 类中,@Aspect注解标示该类为切面类;@Before注解标示前置通知, 在方法执行之前执行;@Afte注解标示后置通知, 在方法执行之后执行。

AspectJ 支持 5 种类型的通知注解:

@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行(无论是否发生异常)
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

可以在通知方法中声明声明一个类型为JoinPoint的参数,这样我们就可以访问到连接细节信息,如方法名称、参数值等。

以@Before(“execution(String com.ricky.codelab.spring.aop.service.UserServiceImpl.login(String,String))”)表达式为例
这里写图片描述

AspectJ 切入点表达式

execution:表示在方法执行时触发
修饰符及返回值类型:表示方法修饰符及返回值类型,* 代表任意修饰符及任意返回值类型
方法名:包括方法所属类名与方法名
参数:表示方法形参,.. 匹配任意数量的参数

4、在配置文件中启用AspectJ自动代理

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns = "http://www.springframework.org/schema/beans" 
     xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:aop = "http://www.springframework.org/schema/aop" 
     xmlns:context = "http://www.springframework.org/schema/context" 
     xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" >
 
     <!-- 自动扫描的包 --> 
     < context:component-scan   base-package = "com.ricky.codelab.spring.aop" > </ context:component-scan >
 
     <!-- 启用AspectJ自动代理 --> 
     < aop:aspectj-autoproxy > </ aop:aspectj-autoproxy >
 
</beans>

重用切入点表达式

为了重用切入点表达式,我们可以使用@Pointcut注解来声明切入点表达式,然后在其他地方进行使用,如下:

package  com.ricky.codelab.spring.aop;
 
 import  java.util.Arrays;
 import  java.util.List;
 import  org.aspectj.lang.JoinPoint;
 import  org.aspectj.lang.annotation.Aspect;
 import  org.aspectj.lang.annotation.Before;
 import  org.aspectj.lang.annotation.Pointcut;
 import  org.springframework.stereotype.Component;
 
 @Aspect 
 @Component 
 public    class   LogDeclareAspect  { 
 
     /**
     * 使用@Pointcut来声明切入点表达式
     * 定义一个方法用来声明切入点表达式,一般地,该方法中不需要填入其它代码
     */ 
     @Pointcut ( "execution(String com.ricky.codelab.spring.aop.service.UserService.login(String,String))" )
     public   void   declareJoinPointExpression (){
    }
 
     /**
     * 前置通知:在方法执行前执行的代码
     *  @param  joinpoint
     */ 
     @Before ( "declareJoinPointExpression()" )
     public   void   beforeExecute (JoinPoint joinPoint){
 
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
 
        System.out.println( "The method " +methodName+  " begin with " +args);
    }
}

切面优先级

在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的。可以使用@Order注解指定切面的优先级,值越小优先级越高。

@Order ( 1 )
@Aspect 
@Component 
public class LogAspect  { 
} 

IOC容器的生命周期

一、Bean生命周期

Spring IOC容器可以管理Bean的生命周期,允许在Bean生命周期的特定点执行定制的任务。

Spring IOC容器对Bean的生命周期进行管理的过程如下:

1.通过构造器或工厂方法创建Bean实例
2.为Bean的属性设置值和对其它Bean的引用
3.调用Bean的初始化方法
4.Bean可以使用了
5.当容器关闭时,调用Bean的销毁方法

具体可以看到
首先通过构造函数来创建bean的实例,然后通过setter方法设置属性,在使用bean实例之前,执行了init初始化方法,使用之后又执行了destroy方法

二、创建Bean后置处理器

Bean后置处理允许在调用初始化方法前后对Bean进行额外的处理,Bean后置处理器对IOC容器的所有Bean实例逐一处理,而非单一实例。其典型应用是:检查Bean属性的正确性或根据特定的标准更改Bean的属性。

对Bean后置处理器而言,需要实现接口BeanPostProcessor,在初始化方法被调用前后,Spring将把每个Bean实例分别传递给上述接口的以下两个方法:
1.通过构造器或工厂方法创建 Bean 实例
2.为 Bean 的属性设置值和对其他 Bean 的引用
3.将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
4.调用 Bean 的初始化方法
5.将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
6.Bean 可以使用了
7.当容器关闭时, 调用 Bean 的销毁方法

基于注解配置Bean

第一步:组件扫描
在classpath中扫描组件
组件扫描(component scanning):Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件。
特定的组件包括:
-@Component:基本注解,标识了一个受Spring管理的组件
-@Responsitory:标识持久层组件
-@Service:标识服务层(业务层)组件
-@Controller:标识表现层组件

【本质上所有的特定组件可以混用,因为在IOC里面没有绝对性的对这几个组件进行名称上的区分,但是建议按照标准进行使用】
对于扫描到的组件,Spring有默认的命名策略:使用非限定类名,第一个字母小写。也可以在注解中通过value属性值标识组件的名称。
第二步:文件配置
当在组件类上使用了特定的注解之后,还需要在Spring的配置文件中声明context:component-scan:
• base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里及其子包中的所有类
• 当需要扫描多个包时,可以使用逗号分隔
• 如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类
,示例:

<context:component-sacn base-package=“com.yl.spring.beans” resource-pattern=“autowire/*.class”/>
context:include-filter子节点表示要包含的目标类
context:exclude-filter子节点表示要排除在外的目标类

context:component-sacn下可以拥有若干个context:include-filter和context:exclude-filter子节点
同时:
context:include-filter和context:exclude-filter子节点支持多种类型的过滤表达式:

第三步:组件装配
context:component-scan元素还会自动注册AutowiredAnnotationBeanPostProcessor实例,该实例可以自动装配具有@Autowired和@Resource、和@Inject注解的属性
使用@Autowired自动装配bean
@Autowired注解自动装配具有兼容类型的单个bean属性
-构造器,普通字段(即使是非public),一切只有参数的方法都可以应用@Autowired
-默认情况下,所有使用@Autowired注解的属性都需要被设置,当Spring找不到匹配的bean装配属性时,会抛出异常。若某一属性允许不被设置,可以设置@Autowired注解的required属性为false
-默认情况下,当IOC容器里存在多个类型兼容的bean时,通过类型的自动装配将无法工作。此时可以在@Qualifiter注解里提供bean的名称,Spring允许对方法的入参标注 @Qualifiter已指定注入bean的名称
-@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动匹配
-@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean
-@Autowired注解用在java.util.Map上时,若该Map的键值作为String,那么Spring将自动装配与之Map值类型兼容的bean,此时bean的名称作为键值
使用@Resource或@Inject自动装配bean
Spring还支持@Resource和@Inject注解,这两个注解和@Autowired注解的功用类似
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称
@Inject和@Autowired注解一样也是按类型注入的bean,但是没有required属性


四、总结

@Autowired 注解自动装配具有兼容类型的单个 Bean属性

构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解。

默认情况下, 所有使用 @Authwired 注解的属性都需要被设置. 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为false。

默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作. 此时可以在@Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter已指定注入 Bean 的名称。

@Authwired 注解也可以应用在数组类型的属性
  • List item

上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配。

@Authwired注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean。

@Authwired 注解用在 java.util.Map 上时, 若该Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

SPEL

一、SpEL简介

1、SpEL(Spring Expression Language):是一个支持运行时查询和操作对象图的强大的表达式语言。

2、类似于EL:SpEL使用 #{…} 作为定界符,在所有在 { } 中的字符都认为是SpEL。

二、SpEL表示字面值

1、字面值:可用字符串表示的值。

2、Spring配置Bean的属性时,可以使用SpEL表示字面值:

<!-- 整数-->
<property name="intNum" value="#{2}"/>
<!-- 浮点数-->
<property name="floatNum" value="#{2.1}"/>
<!-- 科学计数法-->
<property name="scienceNum" value="#{1e4}"/>
<!-- 字符串,单引号-->
<property name="stringType" value="#{'Mike'}"/>
<!-- 字符串,双引号-->
<property name="stringType2" value='#{"Mike"}'/>
<!-- 布尔值-->
<property name="booleanValue" value="#{true}"/>

三、SpEL引用Bean、属性和方法

1、引用其Bean

2、引用其他Bean的属性

3、调用其他方法

四、SpEL中使用运算符

1、算术运算符:+ - * / % ^:

<!-- 加法-->
<property name="plus" value="#{2 + 2}"/>
<!-- 减法-->
<property name="sub" value="#{2 - 1}"/>
<!-- 乘法-->
<property name="mul" value="#{3 * 4}"/>
<!-- 除法-->
<property name="div" value="#{10 / 2}"/>
<!-- 取余-->
<property name="remainder" value="#{9 % 2}"/>
<!-- 次方-->
<property name="power" value="#{3 ^ 5}"/>



<!-- 加号还可以作为字符串连接符-->
<property name="strConnect" value="#{'hello' + ' ' + 'world'}"/>

2、比较运算符:< > == <= >= lt gt eq le ge

<!-- == -->
<property name="equals" value="#{numCount.intNum == 100}"/>
<!-- <= -->
<property name="lessOrEqual" value="#{numCount.intNum le 100}"/>

3、逻辑运算符:and,or,not

4、三目运算符:? :

5、正则表达式:matches

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值