[Java]Spring框架学习笔记

Spring 框架

  • Spring概述
  • IOC
    • bean属性
    • bean的生命周期
    • 创建bean
    • 给bean注入值
    • 使用注解创建bean(官方推荐)
      • 添加注解
      • 引入context名称空间自动扫描
      • 使用@Autowired注解实现根据类型自动装配
      • 一定要导入aop包,支持加注解模式
      • Spring的单元测试
  • AOP
    • 术语解释
    • 使用步骤
    • 基于注解的AOP
      • 将切面类加载入ioc容器
      • AOP的5个注解
        • 书写切点表达式
        • 得到方法返回值
        • 得到方法执行时的异常
        • 环绕通知
          • 按方法执行顺序输出结果
          • 环绕通知和其他通知混合时的执行顺序
          • 注意事项
      • 多切面时的执行顺序
    • 基于配置文件的AOP
  • 参考资料

Spring概述

Spring模块示意图:

Core Container:核心容器(IOC)
AOP+Aspects:面向切面编程模块
DataAccess/Integration:数据访问模块
Web:Spring开发Web应用模块

IOC

控制反转——控制并反转资源的获取方式,由程序员自己创建资源转变为由容器创建和设置资源。
DI: Dependency Injection 依赖注入
spring这个容器中,替你管理着一系列的类,前提是你需要将这些类交给spring容器进行管理,然后在你需要的时候,不是自己去定义,而是直接向spring容器索取,当spring容器知道你的需求之后,就会去它所管理的组件中进行查找,然后直接给你所需要的组件.

bean属性

id:bean的唯一标识
class:组件的全类名

bean的生命周期

通过scope属性设置bean的生命周期:
prototype 多实例:
1.在容器启动时不会创建实例,从容器中获取bean时才创建实例
2.每一次获取实例的结果都不一样
singleton 单实例:
1.在容器启动完成之前就创建好bean的实例
2.每一次获取实例的结果都是之前创建的实例
request:
Web环境下,对象与request生命周期一致
session:
Web环境下,对象与session生命周期一致
生命周期属性:
初始化方法:init-method
销毁方法:destroy-method


    
        
    

创建bean

1.创建一个空的bean:


    

2.利用静态工厂创建bean:


    
    

3.利用实例工厂创建bean:


    
    
    

给bean注入值

1.set注入:
基本类型使用value注入,引用类型使用ref注入,默认注入null,若要给有默认值的属性注入null,则使用null标签.


        
        
        
        
        
        
            
        
    

2.使用有参构造器注入:
在bean中定义一个有参构造函数.

public Person(String name, Integer age, String gender, String email) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.email = email;
        System.out.println("调用有参构造器...");
    }

配置文件:


        
        
        
        
        
        
    

3.名称空间注入
引入p名称空间:在beans标签中加入

xmlns:p="http://www.springframework.org/schema/p"

    

4.SPEL注入:


    
    
        
        
        
        
        
        
        
        
        
        
    

5.注入list:


        
        
            
                
                
                
                
            
        
    

6.注入map:


        
        
            
                
                
                
                
            
        
    

使用utils名称空间注入:
引入utils名称空间


       xmlns:utils="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-4.0.xsd">

定义一个map:


        
        
        
    

定义bean:


        
        
    

7.注入properties:


        
            
                root
                123456
            
        
    

8.注入级联属性:


        
        
    

9.加载外部配置文件:


使用注解创建bean(官方推荐)

使用注解将组件快速的加入到容器中需要:

添加注解

spring有四个注解:
@Controller 控制器 给控制器(servlet)层的组件加
@Service 业务逻辑
@Repository 给数据库层(持久化,Dao层)的组件添加这个注解
@Component 给不属于以上几层的组件添加这个注解
注解可以随便加,spring底层不会验证组件是否如注解所说.

1.组件的id默认就是组件的类名首字母小写,设置组件id在注解后加括号,括号中设置id.
@Repository(“id”)
2.组件的作用域,默认是单例的
@Scope(value=“prototype”) //设置为多实例

引入context名称空间自动扫描

context:component-scan:自动组件扫描
base-package:指定扫描的基础包,spring会将基础包及下面所有包中加了注解的类自动扫描进ioc容器


   
   
       
       
   

   
       
       
       
   

使用@Autowired注解实现根据类型自动装配


            
            
            
//自动装配,自动的为这个属性赋值
    //@Qualifier:指定一个名字作为id,让spring别使用变量名作为id.
    @Qualifier("bookService")
    //@Autowired(required = false) 找不到就装配null
    @Autowired
    private BookService bookServiceExt2;

在方法参数上添加@Qualifer注解:

/**
     * 方法上有autowired,这个方法的每一个参数都会注入值,且会在创建bean时自动运行
     */
    @Autowired
    public void hahaha(BookDao bookDao, @Qualifier("bookService")BookService bookService){
        System.out.println("Spring运行了这个方法..." + bookDao + "," + bookService);
    }

一定要导入aop包,支持加注解模式

使用注解加入到容器中的组件,和使用配置加入到容器中的组件行为是默认一样的

Spring的单元测试

@RunWith(SpringJUnit4ClassRunner.class)//spring测试包
@ContextConfiguration(“classpath:applicationContext.xml”) //配置文件的地址

AOP

面向切面编程:指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行

术语解释

JoinPoint(连接点):目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。

Pointcut(切入点):目标对象中,已经被增强的方法。调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。

Advice(通知/增强) :增强方法的代码、想要的功能。

Target(目标对象):被代理对象,被通知的对象,被增强的类对象。

Weaving(织入):将通知应用到连接点形成切入点的过程

Proxy(代理):将通知织入到目标对象之后形成的代理对象

aspect(切面):切入点+通知————通知(Advice)说明了干什么的内容(即方法体代码)和什么时候干(什么时候通过方法名中的before,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),切点表达式等定义。

使用步骤

1.导包
spring-aop + spring-aspects + springsource.org.aopalliance + springsource.org.aspectj.weaver
2.写配置
1,将目标类和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中
2.告诉Spring哪个是切面类
3.告诉Spring,切面类里面的每一个方法都是何时何地运行
4.开启基于注解的AOP功能(或使用配置文件)
3.测试
重要的用配置,不重要的用注解

基于注解的AOP

先引入AOP名称空间,再加上下面代码:

开启使用注解。

将切面类加载入ioc容器

使用@Aspect注解可以将切面类加载入ioc容器
使用@Order(Integer)注解可以改变切面加载顺序,数值越小越优先

AOP的5个注解

5个通知注解:
前置通知:@Before():在目标方法之前运行
后置通知:@After():在目标方法结束之后运行
返回通知:@AfterReturning():在目标方法正常返回之后
异常通知:@AfterThrowing():在目标方法抛出异常之后运行
环绕通知:@Around():围绕方法执行

try{
           //@Before
           method.invoke();
           //@AfterReturning
      }catch(e){
          //@AfterThrowing
      }finally{
          //@After
  }

书写切点表达式

上述5个注解在()内设置value值可指定切点位置

/**
     * 方法切入点表达式:execution(访问权限符 返回值类型 方法签名)
     * 支持通配符:
     *  *:
     *      1.匹配一个或多个字符:execution(public int com.third.impl.MyMathCalculator.*(int,int))
     *      2.匹配任意一个参数:
     *      3.只能匹配一层路径
     *      4.权限位置不能用*号表示,想要表示任意权限,不写就行
     *  ..:
     *      1.匹配任意多个参数和任意类型参数
     *      2.匹配任意多层路径
     *  支持&&,||,!运算
     */
/**
     * 抽取可重用的切入点表达式:
     * 1.随便声明一个没有实现的返回void的方法
     * 2.给方法上标注@Pointcut注解
     */
    @Pointcut("execution(public int com.third.impl.MyMathCalculator.*(int,int))")
    public void hahaMyPoint(){};

得到方法返回值

使用@AfterReturning注解中的returning属性可以获取方法调用后的返回值.
eg:

/**
     * 使用AfterReturning注解中的returing来接收返回值,returing = 接收变量名称
     * @param joinPoint 连接点
     * @param result 返回值
     */
    @AfterReturning(value = "hahaMyPoint()",returning = "result")
    public static void logReturn(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[Logs][" + name +"]方法正常执行完成,结果为[" + result + "]");
    }

得到方法执行时的异常

/**
     * 在通知方法运行时,拿到目标方法的详细信息
     * 1.给通知方法的参数列表上添加JoinPoint参数:
     * 2.使用throwing告诉spring接收异常的变量
     * 3.Exception尽量往大了写,写小了便只能接收那一类的异常
     */
    @AfterThrowing(value = "hahaMyPoint()",throwing = "e")
    public static void logException(JoinPoint joinPoint, Exception e){
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println("[Logs][" + name + "]方法出现异常,异常信息是:" + e.getMessage());
    }

环绕通知

环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
eg:

@Around("hahaMyPoint()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
        Object[] args = pjp.getArgs();
        Object proceed = null;
        try {
            System.out.println("方法前置通知");
            //就是利用反射调用目标方法
            proceed = pjp.proceed(args);
            System.out.println("方法正常执行通知");
        } catch (Exception e){
            System.out.println("方法异常执行通知");
            e.printStackTrace();
            throw new RuntimeException(e);//不加这行外界感受不到
        } finally {
            System.out.println("方法结束执行通知");
        }
        return proceed;
    }
按方法执行顺序输出结果

在使用除环绕通知外的其他四个通知时,方法的输出如下:

/**
     * 通知方法的执行顺序:
     * 正常执行:@Before(前置通知) =====> @After(后置通知) =========> @AfterReturning(正常返回)
     * 异常执行:@Before(前置通知) =====> @After(后置通知) =========> @AfterThrowing(方法执行异常)
     */
[Logs][add]方法开始执行,用的参数列表[[2, 13]]
[Logs][add]执行完成
[Logs][add]方法正常执行完成,结果为[15]

使用环绕通知时,方法的输出如下:

方法前置通知
方法正常执行通知
方法结束执行通知
15
环绕通知和其他通知混合时的执行顺序

环绕通知优于其他通知执行。
顺序:
(环绕前置->普通前置)->目标方法执行->环绕正常返回/异常执行->环绕后置->普通后置->普通返回或异常
有时可以普通前置在前面执行。

注意事项

对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。

多切面时的执行顺序

与栈的顺序一样,先进后出
环绕通知只影响当前切面。

基于配置文件的AOP

在bean配置文件中,所有的Spring AOP配置都必须定义在元素内部。对于每个切面而言,都要创建一个元素来为具体的切面实现引用后端bean实例。
eg:



        

        

            

            

            

            

            
        

    

事务部分待补充
To be continue…

参考资料

1.https://blog.csdn.net/itcats_cn/article/details/81479185
2.https://www.bilibili.com/video/av71336532/

你可能感兴趣的:([Java]Spring框架学习笔记)