Spring详解(IOC和AOP)

目录

一,什么是Spring?

二,控制反转(IOC)

2.1 控制反转是什么?

2.2 如何实现控制反转?

2.3 依赖注入

2.3.1 set注入简单类型

2.3.2 注入Bean

 2.3.3 注入数组

 2.3.4 注入List集合

 2.3.5 注入Set集合

 2.3.6 注入Map集合

 2.3.7 注入Properties

2.4 Bean的作用域

2.5 IOC的注解式开发

2.5.1声明Bean的注解常见的有四个

2.5.2 spring给出四个注解用来给属性赋值:

 2.6 IOC的全注解开发

三,AOP

 3.1:AOP中的名词解释

3.2切入点表达式

 3.3 基于AspectJ的AOP注解式开发

 3.4 通知类型

3.4.1 通知的执行顺序

3.4.2 有多个切面时,切面的执行顺序

3.5全注解式开发AOP

3.6 AOP的XML开发(了解)


一,什么是Spring?

百度百科这样形容Spring:Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC

总之:Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的框架。

二,控制反转(IOC)

2.1 控制反转是什么?

  • 控制反转是一种思想。
  • 控制反转是为了降低程序耦合度,提高扩展度,达到OCP原则,达到DIP原则。
  • 控制反转就是把对象的控制权交出去,交给容器(这里是Spring容器)进行管理

2.2 如何实现控制反转?

  • DI(Dependency Injection):依赖注入
  • 依赖注入通常有两种方法:set注入和构造方法注入

2.3 依赖注入

  1. 构造方法注入
    1. 通过构造方法来给属性赋值
  2.  set方法注入
    1. set方法注入,是基于set方法实现的,底层会通过反射机制调用该属性的set方法给属性进行赋值,这种方法要求该属性必须提供对外的set方法。

下面分别演示两种注入方法:

先创建一个Maven工程:

Spring详解(IOC和AOP)_第1张图片

点击下一步并命名项目: Spring详解(IOC和AOP)_第2张图片

 然后无脑下一步即可。

这时来准备我们的依赖,因为是maven工程,所以这里只给出坐标


        
            junit
            junit
            4.11
            test
        
        
            org.springframework
            spring-context
            5.3.22
        

 这里引入spring-context后会关联引入一些其他的jar包,如下图:

Spring详解(IOC和AOP)_第3张图片

 然后就可以写代码了。

构造方法注入是spring容器通过构造方法来给属性赋值、所以类必须有构造方法

先写一个实体类:

package com.hkd.Bean;

public class user {
    String name;
    int age;

    public user() {
    }

    public user(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "user{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

 写好实体类后就可以 编写Spring配置文件了。

在IDEA中文件的目录结构如下:

Spring详解(IOC和AOP)_第4张图片

spring配置文件如下:




    
        
        
    


 运行后可看到结果:

Spring详解(IOC和AOP)_第5张图片

 这里constructor-arg标签中的index属性指的是,参数在构造方法中的顺序。

除了index可以指定参数外也可用参数的名称来指定:

Spring详解(IOC和AOP)_第6张图片

 如果需要的参数是引用类型则按照下面写法:

Spring详解(IOC和AOP)_第7张图片

2.3.1 set注入简单类型

        简单类型包括:

  • 基本本数据类型 
  • 基本数据类型对应的包装类
  • String或其他的CharSequence⼦类
  • Number⼦类
  • Date⼦类
  • Enum⼦类
  • URI
  • URL
  • Temporal⼦类
  • Locale
  • Class
  • 另外还包括以上简单值类型对应的数组类型

简单类型注入的代码大致为:

     
        
     

 在这里有几个标签:

  • bean:具有id属性和class属性,id是spring容器帮忙创建的对象的名称这里的user等同于t通过user user =  new user();创建的user对象,class的值表示要创建的对象的类路径,spring容器拿到这个路径后会通过反射机制创建该类的对象。
  • property:property单词本身就有属性性质的意思,这里指的也是对象中的属性,其name属性值为对象的属性名,value值就是要给对象中属性赋予的值。

先创建一个实体类,提供set和构造方法

package com.hkd.Bean;

public class user {
    String name ;
    int age;
    
    public user() {
    }

    public user(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "user{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

spring配置文件如下:



    
        
        
     

到这里准备工作完成可以进行set注入的测试了:

写一个测试类如下:

        

package myTest;

import com.hkd.Bean.user;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DiTest {
    @Test
    public void TestSetDI(){
        ApplicationContext ioc  = new ClassPathXmlApplicationContext("applicationContext.xml");
        user user = ioc.getBean("user", user.class);
        System.out.println(user);

    }
}

 控制台可看到结果为:

Spring详解(IOC和AOP)_第8张图片

简单类型注入成功。

2.3.2 注入Bean

        如果在一个对象在另一个对象中作为一个属性,那么在注入的时候就不能用和简单类型一样的方法进行注入了,此时的注入格式如下:

    
            
    

 这里可以看到相比于注入简单类型,注入Bean知识将value=""换为了ref="",此时ref=""中的值应填写为一个Bean的id就是spring容器管理的一个对象的名称。

如在刚才的程序中添加一个Vip类,类中有一个user属性,则可以按照一下方式写:

VIP类:

package com.hkd.Bean;

public class Vip {
    int id;
    int balance;
    user user;

    public Vip() {
    }

    public Vip(int id, int balance) {
        this.id = id;
        this.balance = balance;
    }

    public void setUser(com.hkd.Bean.user user) {
        this.user = user;
    }

    public void setId(int id) {
        this.id = id;
    }
    
    public void setBalance(int balance) {
        this.balance = balance;
    }
    @Override
    public String toString() {
        return "Vip{" +
                "id=" + id +
                ", balance=" + balance +
                ", user=" + user +
                '}';
    }
}

Spring配置文件: 



    
        
        
     
    
        
        
        
    
   

在控制台可以看到user1对象被成功注入到了Vip了中。                       

 Spring详解(IOC和AOP)_第9张图片

 除了这种方式还有一种注入Bean的方式:

Spring详解(IOC和AOP)_第10张图片

 2.3.3 注入数组

        当输入的数组类型是简单类型时:

    
        
            
                抽烟
                喝酒
                烫头
            
        
    

非简单类型时:

     
        
            
                
                
                
            
        
    

 2.3.4 注入List集合

    
        
            
                抽烟
                喝酒
                烫头
            
        
    

 注:当List集合中是简单类型时使用value标签反之使用ref标签

 2.3.5 注入Set集合

      
        
            
                抽烟
                喝酒
                烫头
            
        
    

 注:当Set集合中是简单类型时使用value标签反之使用ref标签 

 2.3.6 注入Map集合

    
        
            
                
                
                
            
        
    

 要点:

  • 如果key是非简单类型,则使用key-ref
  • 如果value是非简单类型,则使用value-ref

 2.3.7 注入Properties

    
        
            
                com.mysql.cj.jdbc.Driver
                jdbc:mysql://localhost:3306/spring
                root
                123456
            
        
    

2.4 Bean的作用域

        Spring创建的对象默认是单例的。

        如果想让对象是多例的可以在指定scope的属性值为prototype,这样每调用一次getBean方法都会新创建一个对象。

Spring详解(IOC和AOP)_第11张图片

 默认情况下scope的值为singleton,即为单例

2.5 IOC的注解式开发

2.5.1声明Bean的注解常见的有四个

  • @Component        
  • @Controller      (用于表示层)
  • @Service          (用于业务层)
  • @Repository    (用于持久层)

这四个注解中@Controller,@Service,@Repository是@Component的别名, 四个注解用法相同,对程序来说这三个都是一样的,那么为什么还要搞三个别名呢--这是给程序员看的,为了提高高代码的可读性。

这里拿@Component注解举例,其他三个用法相同:

@Component注解写在类上

package com.hkd.Bean;

import org.springframework.stereotype.Component;

@Component(value = "person")
public class preson {
    String name;
}

 这里的value值就是spring容器在创建对象时的对象名,可以忽略不写,此时对象名为首字母小写的类名

当然仅仅在类中写上注解还不行,spring容器怎么样才能发现,或者说spring容器怎么判断程序员是否写了注解呢?

这时要在spring的核心配置文件中告诉spring容器去哪些包中找带有注解的类,即需要添加包扫描,spring容器会到对应的包中检查是否写了注解,如果写了注解,那就创建对象。

 如果是多个包有两种解决⽅案:

  • 第⼀种:在配置⽂件中指定多个包,⽤逗号隔开。
  • 第⼆种:指定多个包的共同⽗包。

 此时需要添加context命名空间

Spring详解(IOC和AOP)_第12张图片

 至此对象就可以成功创建了,那有了对象要怎么给属性赋值呢?

2.5.2 spring给出四个注解用来给属性赋值:

  • @Value
  • @Autowried
  • @Qualifier
  • @Resource

 @Value : 当属性的类型是简单类型时使用@Value进行注入,

@Value可以写在属性上,构造方法里,set方法上,且当@Value写在属性上时可以不提供set方法

@Autowried :当属性的类型是引用类型时使用@Autowried注入(按类型注入)

@Autowried注解可以标注在哪⾥?

  • 构造⽅法上
  • ⽅法上
  • 形参上
  • 属性上
  • 注解上         

该注解有⼀个required属性,默认值是true,表示在注⼊的时候要求被注⼊的Bean必须是 存在的,如果不存在则报错。如果required属性设置为false,表示注⼊的Bean存在或者不存在都没 关系,存在的话就注⼊,不存在的话,也不报错。 

因为@Autowried是按类型注入的,所以如果在spring容器中有多个相同类型的对象的话,@Autowried在注入时就会发生混乱,此时程序会报错。

Spring详解(IOC和AOP)_第13张图片

这里的bigBoss和littleBoss是Boss的实现类

Spring详解(IOC和AOP)_第14张图片

 当我们进行注入的时候就会报错,发现了两个对象。

 有没有办法解决这个问题呢?

肯定是有的,还有一个@Qualifier,这两个注解进行搭配使用就可以根据名称进行注入。

我们将上面代码稍加改动

Spring详解(IOC和AOP)_第15张图片

此时再运行代码就不会出错且在控制台输出:

@Resource:@Resource也可以完成⾮简单类型注⼊。

                        那它和@Autowired注解有什么区别?

  •  Resource注解是JDK扩展包中的,也就是说属于JDK的⼀部分。所以该注解是标准注解,更加具 有通⽤性。@Autowired注解是Spring框架⾃⼰的。
  • @Resource注解默认根据名称装配byName,未指定name时,使⽤属性名作为name。通过name 找不到的话会⾃动启动通过类型byType装配。 @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解⼀起 ⽤。
  • @Resource注解⽤在属性上、setter⽅法上。 @Autowired注解⽤在属性上、setter⽅法上、构造⽅法上、构造⽅法参数上。
  • @Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引⼊依赖:【如果是JDK8的话不需 要额外引⼊依赖。⾼于JDK11或低于JDK8需要引⼊以下依赖。

 javax.annotation
 javax.annotation-api
 1.3.2

 2.6 IOC的全注解开发

IOC的全注解开发就是不再写XML配置文件了,而是写一个类来代替XML配置文件

package com.hkd.Bean;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.hkd.Bean")
public class SpringConfig {
}

这时候我们不再创建ClassPathXmlApplicationContext对象了

Spring详解(IOC和AOP)_第16张图片

 除了创建的对象不一样之外,其余操作与XML写法一致。

三,AOP

 3.1:AOP中的名词解释

  • 连接点 Joinpoint :在程序的整个执⾏流程中,可以织⼊切⾯的位置。⽅法的执⾏前后,异常抛出之后等位置。
  • 切点 Pointcut: 在程序执⾏流程中,真正织⼊切⾯的⽅法。(⼀个切点对应多个连接点)。
  • 通知 Advice:通知⼜叫增强,就是具体你要织⼊的代码。 通知包括: 前置通知 后置通知 环绕通知 异常通知 最终通知。
  • 切⾯ Aspect:  切点 + 通知就是切⾯。
  • 织⼊ Weaving: 把通知应⽤到⽬标对象上的过程。
  • 代理对象 Proxy: ⼀个⽬标对象被织⼊通知后产⽣的新对象。
  • ⽬标对象 Target: 被织⼊通知的对象。

3.2切入点表达式

什么是切入点表达式:就是表明你想要在哪个方法上织入切面(添加代码,增强功能)

execution([访问控制权限修饰符] 返回值类型 [全限定类名]⽅法名(形式参数列表) [异常])
  •  访问控制权限修饰符: 可选项。 没写,就是4个权限都包括。 写public就表示只包括公开的⽅法。
  • 返回值类型: 必填项。 * 表示返回值类型任意。
  • 全限定类名: 可选项。 两个点“..”代表当前包以及⼦包下的所有类。 省略时表示所有的类。
  • ⽅法名: 必填项。 *表示所有⽅法。 set*表示所有的set⽅法。
  • 形式参数列表: 必填项 。表示没有参数的⽅法 (..) 参数类型和个数随意的⽅法 (*) 只有⼀个参数的⽅法 (*, String) 第⼀个参数类型随意,第⼆个参数是String的。
  • 异常: 可选项。 省略时表示任意异常类型。

 3.3 基于AspectJ的AOP注解式开发

先写一个目标类:

package com.hkd.AspectJ.Service;

import org.springframework.stereotype.Service;

@Service
public class orderService {
    public void Buy(){
        System.out.println("买东西...");
    }
}

再写一个切面类:

package com.hkd.AspectJ.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class myAspect {

    @Before("execution(* com.hkd.AspectJ.Service.*.*(..))")
    public void myBefore(){
        System.out.println("前置通知执行了...");
    }
}

Spring.xml:



    
    

    

这里看到在spring.xml文件中多了这样的一行代码

这句的作用是开启aop的自动代理添加完这句代码后所有带有@Aspect注解的都会生成一个代理对象

测试:

Spring详解(IOC和AOP)_第17张图片

 Spring详解(IOC和AOP)_第18张图片

 3.4 通知类型

通知类型包括:

  • 前置通知:@Before ⽬标⽅法执⾏之前的通知
  • 后置通知:@AfterReturning ⽬标⽅法执⾏之后的通知
  • 环绕通知:@Around ⽬标⽅法之前添加通知,同时⽬标⽅法执⾏之后添加通知。
  • 异常通知:@AfterThrowing 发⽣异常之后执⾏的通知
  • 最终通知:@After 放在finally语句块中的通知

@Pointut注解:可以写在一个方法上作为一个切点表达式

@Pointcut("execution(* com.hkd.AspectJ.Service.*.*(..))")
    public void pointCut(){}

此时pointCut()等同于execution(* com.hkd.AspectJ.Service.*.*(..))

3.4.1 通知的执行顺序

此时我们来看无异常时通知的执行顺序:

package com.hkd.AspectJ.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class myAspect {

    @Pointcut("execution(* com.hkd.AspectJ.Service.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void myBefore(){
        System.out.println("前置通知执行了...");
    }
    @AfterReturning("pointCut()")
    public void myAfterReturning(){
        System.out.println("后置通知执行了...");
    }
    @Around("pointCut()")
    public void myAround(ProceedingJoinPoint point) throws Throwable{
        System.out.println("环绕通知开始...");
        point.proceed();
        System.out.println("环绕通知结束...");
    }
    @After("pointCut()")
    public void myAfter(){
        System.out.println("最终通知执行了...");
    }

}

测试结果:Spring详解(IOC和AOP)_第19张图片

 由此可以看出最先执行的是环绕通知,然后是前置通知,在执行目标方法,紧接着后置通知,最终通知。

当有异常时:

我们在切面类中添加异常通知,这里不做演示

在Service类中添加一条异常语句如下图:Spring详解(IOC和AOP)_第20张图片

 此时再进行测试:

Spring详解(IOC和AOP)_第21张图片

从结果中我们可以看到,程序到达错误语句时报错且除了最终通知外后面的通知不再执行, 由此可见最终通知的作用类似finally代码块,这里可以写一些必须执行的代码。

3.4.2 有多个切面时,切面的执行顺序

要搞清楚切面的执行顺序,我们先来看一个注解@Order,@Order注解可以指定一个整形的数字,数字值越小,优先级越高。

我们再编写一个切面类aspect2,aspect和aspect2:分别如下:

Spring详解(IOC和AOP)_第22张图片

Spring详解(IOC和AOP)_第23张图片

 除了@Order值不同外,其余都一致。

测试可得到结果:Spring详解(IOC和AOP)_第24张图片

 由此我们可以清楚地看到切面的执行顺序。

3.5全注解式开发AOP

与全注解开发IOC时相同,同样创建一个类作为spring容器的配置文件,但是AOP要多添加一条自动代理的语句,添加了这条语句spring才能自动的帮你创建切面类:

Spring详解(IOC和AOP)_第25张图片

 Spring详解(IOC和AOP)_第26张图片

 只需要添加一条@EnableAspectJAutoProxy就可以实现自动代理,是不是很简单

这里简单的提一下,@EnableAspectJAutoProxy有一个ProxyTargetClass属性,为布尔类型,当其值为true的时候,表示使用cglib动态代理,为false时使用JDK动态代理,JDk动态代理只能代理接口,cglib动态代理既能代理接口又能代理了类,当值为false时,如果没有接口则还是会代理类,在Spring5中AOP的默认代理方式是JDK动态代理。在XML配置中自动代理的标签也有这样的属性

 
  

3.6 AOP的XML开发(了解)

直接编写Spring配置:




    
    
    




    

        


        

            
         
    

进行测试: 

Spring详解(IOC和AOP)_第27张图片

 成功得到结果:

Spring详解(IOC和AOP)_第28张图片

那么到这里对IOC的AOP的大致内容便结束了,这只是我个人的一篇学习笔记,如果有错误,还希望大家能够指正,我们一起进步!!

你可能感兴趣的:(java学习笔记,spring,java,后端)