spring学习笔记

目录

概述

什么是spring

侵入式的概念

spring的核心

spring的优势

注意

IOC控制反转

概述

核心

容器

DI,dependency injection依赖注入

概念

注入方式

循环依赖

spring如何解决循环依赖

spring生成Bean的方式

Bean属性注入(Bean属性赋值)

springBean生命周期

实例化Bean

属性赋值(依赖注入)

Aware接口回调

BeanPostProcessor前置处理器

InitializingBean(初始化)

BeanPostProcessor后置处理器

destroy-method销毁

springBean的作用域

springIOC注解

类注解

@Component

@Configuration

@Scope

@Scope具有以下几种作用域

@Scope注意点

属性注解

@Value

@AutoWire

​编辑

@Resource

​编辑

Resource总结

@Autowire和@Resource区别

方法注解

@Bean

@Scope

@Lazy

@Primary

@Profile

AOP面向切面

底层实现

代理

静态代理

动态代理

JDK 动态代理

Cglib 动态代理

动态代理注意点

AOP名词

Joinpoint(连接点)

Pointcut(切入点)

Advice(通知/增强)

Introduction(引介)

Aspect(切面)

Weaving(织入)

Proxy(代理)

简单的springAOP实现

@Aspect

@Pointcut("xxx")

@Before等

其他通知注解

切点表达式

依赖

实现


概述

什么是spring

一站式轻量级分层框架,非侵入式

spring是非侵入式的框架

侵入式的概念

侵入式:实现特定接口,继承特定类,改变原有类的结构,即为侵入式,如struts2

非侵入式:对类原本结构没有影响,仍然增强了JavaBean的功能

spring的核心

核心是IOC控制反转和AOP面向切面

spring的优势

IOC,解耦开发

AOP,方便权限拦截,运行监控

声明式事务管理

方便集成其它框架

降低日常开发难度

注意

因为当下项目已经全面普及springboot,前后端分离,因此我们不再关注以前ssm项目中常见的xml配置文件,比如applicationContext.xml,只关注spring本身

IOC控制反转

概述

在没有spring的情况下,如果需要使用一个类的对象,需要手动的new Xxx(),那么当前类就和Xxx类形成了强耦合

于是有人想到用BeanFactory模式,通过BeanFactory获取对象,这样当前类就和Xxx解耦,比如

private XxDao xxDao = DaoFactory.getInstance().createDao("...dao.impl.XxDaoImpl", XxDao.class);

spring提供了更好的解决方案,即IOC控制反转,将对象的管理权反转给了spring,开发者不再需要手动创建Bean,而是直接从spring容器获取

核心

IOC的核心是spring容器,spring创建对象并负责管理它们完整的生命周期

IOC需要依赖注入(DI)的支持:创建A对象,A会用到其他的类,这时就需要依赖注入支持

容器

ApplicationContext

即时加载,加载applicationContext.xml(容器启动)时候就会创建对象

ClassPathXmlApplicationContext:加载类路径下 Spring 的配置文件.

FileSystemXmlApplicationContext:加载本地磁盘下 Spring 的配置文件

DI,dependency injection依赖注入

概念

依赖:指一个类使用到了另一个类,A类依赖B类,即A类使用到了B类

依赖注入:将类和类的依赖关系告诉spring容器

注入方式

构造方法:通过构造方法将另一个类的对象传入当前类

set方法:通过set方法将另一个类的对象传入当前类

注解注入:通过注解获取另一个类的对象

注解方式只能注入spring容器管理的类,因此要配置扫描包,springboot默认会扫描启动类所在包及其子包下的Bean,可以自定义配置

@ComponentScan(value = {"com.xxx.app.service.*", "com.xxx.app.controller.*"})

public class UserServiceImpl implements UserService {
    //注解注入
    @Autowired
    private UserDao userDao;
    
    //使用构造方法实现依赖注入
    public UserServiceImpl(UserDao userDao){
        this.userDao = userDao;
    }
    
    //使用set方法实现依赖注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

循环依赖

定义:A依赖B,B依赖A

spring如何解决循环依赖

构造器的循环依赖:无法处理,直接抛出BeanCurrentlylnCreationException异常

单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖

非单例循环依赖:无法处理

spring生成Bean的方式

通过无参构造创建Bean

使用BeanFactory:静态和非静态工厂

Bean属性注入(Bean属性赋值)

构造方法:通过构造方法给属性赋值

set方法:通过set方法给属性赋值

spel

springBean生命周期

spring学习笔记_第1张图片

实例化Bean

对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。

对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean

属性赋值(依赖注入)

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入

Aware接口回调

Spring会检测该对象是否实现了xxxAware接口,如果Bean实现了Aware接口,容器会回调相应的方法,将容器相关的信息注入到Bean中

如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字

如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例

如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身

如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文

BeanPostProcessor前置处理器

容器会调用所有实现了BeanPostProcessor接口的类的postProcessBeforeInitialization方法,对Bean进行前置处理

InitializingBean(初始化)

如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法

或在Bean定义中(配置)指定init-method方法

BeanPostProcessor后置处理器

如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法

由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术

在这一步后,Bean就被创建完成,可以开始使用这个Bean了

destroy-method销毁

当容器关闭时,会调用Bean的销毁方法

如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法

或在Bean定义中指定destroy-method方法

springBean的作用域

singleton:默认作用域,单例bean,每个容器中只有一个bean的实例

prototype:为每一个bean请求创建一个实例

request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收

session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例

global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中

springIOC注解

刚刚我们说,IOC是将对象的控制从开发者反转给spring,我们通过spring提供的注解来通知spring管理哪些类,将哪些类以哪种作用域向容器中注入Bean

类注解

@Component

向spring容器注入当前Bean

@Component("user"),相当于xml配置了,现在是否理解xml中的注入?其实就是通过xml的方式向spring容器注入了一个Bean

Spring 中提供@Component 的三个衍生注解,功能和@Component一样

@Controller 控制层

@Service 业务层

加在接口的实现类上,不加在接口上

对于有多个实现类的接口,要指定name,自动装配要使用@Resource(name="xxx")

@Repository 持久层

说明:为了区别于类型,通常配置name属性为开头小写

实现类通常配置name=接口名,存在多个实现类则根据区别配置不同name

@Configuration

标记这是一个配置类,一般会配合@Bean,@Scope,@Lazy使用,等价于原本的xml配置文件注入Bean

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

相当于xml

    
@Scope

@Scope作用在类上,表示给定容器中Bean的作用域

@Scope(scopeName="prototype");

@Scope具有以下几种作用域

singleton 单实例的(单例)(默认),全局有且仅有一个实例

prototype 多实例的(多例),每次获取Bean的时候会有一个新的实例

reqeust   同一次请求,每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效

session   同一个会话级别,每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效

@Scope注意点

1.尽量不要直接使用字符串配置,使用spring提供的参数

ConfigurableBeanFactory.SCOPE_PROTOTYPE

ConfigurableBeanFactory.SCOPE_SINGLETON

WebApplicationContext.SCOPE_REQUEST

WebApplicationContext.SCOPE_SESSION

2.每一个Bean实例,都会有一个initMethod() 和 destroyMethod() 方法,我们可以在Bean 类中自行定义,也可以使用 Spring 默认提供的这两个方法

也可以通过@Bean的属性指定@Bean(initMethod = "initUser", destroyMethod = "destroyUser")

3.思考这么一个场景:当Controller中定义了非静态成员变量,如果不设置成多例,那么多次调用操作同一个变量,会不断的改变变量的值,与我们调用方法的预期不符

属性注解

@Value

用于注入普通类型

@Value(value="zs"),value=可以省略

作用在指定成员的成员变量上或者对应的set()上,区别在于

作用成员变量上,通过反射Field注入

作用在set()上,通过set()注入

可以获取配置文件的数据进行注入,比如.properties

@AutoWire

@Autowire默认按照类型(by-type)装配

默认情况下要求依赖对象必须存在,如果找到多个,再按照名称装配

@Autowire
private StudentService studentService;

如果允许依赖对象为null,需设置required属性为false,即

@Autowire(required=false)
private StudentService studentService;

如果使用按照名称(by-name)装配,需结合@Qualifier注解使用,即

@Autowire
@Qualifier("studentService")    //这里的参数是bean的name,studentService
private StudentService studentService;
spring学习笔记_第2张图片
@Resource

默认按照名称(by-name)装配,名称可以通过name属性指定

当按照名称(by-name)装配未匹配时,按照类型(by-type)装配,此时就是用成员的类型去容器中bean的类型匹配

@Resource(name="studentService")    //用这里的参数studentService去容器和bean的name匹配

当显式指定name属性后,只能按照名称(by-name)装配

如果没有指定name

当@Resource注解在成员变量上时,默认取name=成员变量名称装配,这里是字段名称不是字段类型,如下就是用studentService去和容器中bean的name匹配

当@Resource注解在setter方法上时,默认取name=属性名称装配

@Resource
private StudentService studentService;

@Resource
public void setXXX() {...}
spring学习笔记_第3张图片
Resource总结

如果同时指定name和type属性,则找到唯一匹配的bean装配,未找到则抛异常

如果指定name属性,则按照名称(by-name)装配,未找到则抛异常

如果指定type属性,则按照类型(by-type)装配,未找到或者找到多个则抛异常(一个type可能有多个实现类,所以可能找到多个结果)

既未指定name属性,又未指定type属性,则按照名称(by-name)装配,如果未找到,则按照类型(by-type)装配

@Autowire和@Resource区别

1.来源不同:@Autowire是spring提供的注解,@Resource是jdk提供的注解

2.默认的依赖查找顺序不同:

@Autowire默认根据类型查找,如果找到多个,再根据名称查找

@Resource默认根据名称查找,找不到再根据类型查找

3.支持的参数不同

@Autowire仅支持require参数

@Resource支持多个参数,比如name,type

4.支持的注入方式不同

@Autowire支持属性注入,setter注入,构造注入

@Resource不支持构造注入,即这个标签不能打在构造方法上实现注入

方法注解

@Bean

方法级别注解,通常使用在@Configuration的配置类中使用

向容器注入一个Bean,id为方法名,类型为方法返回值

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

相当于xml

    
@Scope

@Scope作用在方法上,表示当前Bean作用域

@Lazy

@Lazy作用在方法上,表示当前Bean在注入容器时是懒加载模式

@Primary

当某个类型的Bean有多个候选者可以注入时,应当优先加载@Primary标记的Bean

@Profile

当spring.profile.active为对应的值时,才向容器注入当前Bean

AOP面向切面

切面编程并不是spring开创的,来源是java拦截器思想,但spring给切面编程提供了更好的解决方式

抽取程序执行过程中多个部分功能一致的代码,比如:权限校验,日志记录,性能监控,事务控制

底层实现

通过代理机制,springAOP会用到两种代理机制,只能选一种不能混用

代理

什么是代理:将原本的调用,比如person.run();封装一层代理,在代理对象中调用person.run();

静态代理

代码运行前,我们就编写好代理类,编译后生成class文件

缺点十分明显:每一个被代理对象都需要建一个代理类去代理,代码冗杂

动态代理

代理类是在运行过程中产生的,不需要给每一个被代理类编写单独的代理类

JDK 动态代理

针对接口的实现类产生代理

生成接口的实现类,在实现类中通过method.invoke(...)调用被代理对象的方法

Cglib 动态代理

针对没有实现接口的类产生代理

对目标进行继承代理,应用的是字节码增强生成当前类的子类对象

Enhancer类的create()生成代理对象

实现MethodInterceptor接口,重写intercept(...)

methodProxy.invoke(target,objects)调用代理对象方法

如果没有Spring,那么需要我们手动的Proxy.newProxyInstance(xx.xx.xx);

简单来说,就是在目标方法执行时,会通过代理的方式生成一个子类,重写目标方法,在方法体前后执行AOP代码

动态代理注意点

如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

如果目标对象实现了接口,可以强制使用CGLIB实现AOP

如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

Spring AOP的原理是 JDK 动态代理和CGLIB字节码增强技术,前者需要被代理类实现相应接口,也只有接口中的方法可以被JDK动态代理技术所处理;后者实际上是生成一个子类,来覆盖被代理类,那么父类的final方法就不能代理,因为父类的final方法不能被子类所覆盖。一般而言Spring默认优先使用JDK动态代理技术,只有在被代理类没有实现接口时,才会选择使用CGLIB技术来实现AOP,这样就可能造成代理混用的问题,spring提供了一个配置强制使用cglib代理来解决混用问题: 

AOP名词

Joinpoint(连接点)

目标对象中所有可以增强的方法都是连接点

所谓连接点是指那些可以被拦截到的点,因为spring只支持方法类型的连接点

Pointcut(切入点)

目标对象中已经被增强的方法

所谓切入点是指我们要对哪些连接点Joinpoint进行拦截的定义

Advice(通知/增强)

负责增强的代码

所谓通知是指拦截到Joinpoint之后所要做的事情

通知分为前置通知,后置返回通知,后置通知(最终通知),环绕通知(切面要完成的功能),异常通知

后置返回通知:目标方法出现异常不会执行

后置通知(最终通知):目标出现异常也会执行

Introduction(引介)

引介是一种特殊的通知,在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或 Field

Aspect(切面)

是切入点和通知(引介)的结合

Target(目标对象)

被代理的目标对象

Weaving(织入)

将通知应用到切点的过程就是织入

是指把增强应用到目标对象来创建新的代理对象的过程,织入完成生成代理对象

Proxy(代理)

通知织入到目标对象以后,形成代理对象

一个类被AOP织入增强后,就产生一个结果代理类

简单的springAOP实现

@Aspect

定义切面,表示该类是切面类

这个注解只是声明这是个切面类,并没有放进Spring容器,通常使用@Component将该类交给spring容器

切面类中可以定义切点,通知

@Pointcut("xxx")

定义切点,两种方式

execution 指定方法,@Pointcut("execution(* com.coolway.*(..))")

@annotation 指定注解,@Pointcut("@annotation(com.coolway.annotation.RequiresLogin)")

@Before等

定义通知,即指定切点处要执行的AOP代码

要给通知注解传入切点

要么使用上面@Pointcut("xxx")定义好的切点

要么重新传入切点,比如@Around("@annotation(com.coolway.annotation.RequiresLogin)")

其他通知注解

        @AfterReturning

        @Around

        @AfterThrowing

        @After

切点表达式

完整的表达式:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

modifiers-pattern:方法的可见性修饰符,必须是public,通常省略,void com.coolway.service.impl.StudentServiceImpl.getStudent()

ret-type-pattern:方法的返回值类型,如 int,void 等;通常不对返回值做要求,用*表示任意类型返回值,* com.coolway.service.impl.StudentServiceImpl.getStudent()

declaring-type-pattern:方法所在类的全路径名

类名可以定义为部分匹配,比如以ServiceImpl结尾,* com.coolway.service.impl.*ServiceImpl.*(..)

可以配置成包括子包下符合的,使用..符号,如service下任意子包下的impl包的ServiceImpl类的任意方法(任意参数),* com.coolway.service..impl.*ServiceImpl.*(..)

name-pattern:方法名,如 getOrderDetail();可以用*表示该类的任意方法,* com.coolway.service.impl.StudentServiceImpl.*()

param-pattern:方法的参数类型,如 java.lang.String;..表示任意参数,* com.coolway.service.impl.StudentServiceImpl.*(..)

throws-pattern:方法抛出的异常类型,如 java.lang.Exception;

示例:execution(public void com.coolway.service.impl.StudentServiceImpl.getStudent())

依赖


    org.springframework.boot
    spring-boot-starter-aop

实现

@Aspect
public class AspectDemo {
    //定义切点,指定方法
    @Pointcut("execution(* com.coolway.*(..))")

    public void loginAspectPointcut() {}
    
    //使用定义好的切点
    @Before("loginAspectPointcut()")
    public void beforeLogin() {
        System.out.println("开始访问,查看是否登录");
    }
    
    //指定切点为注解
    @After("@annotation(com.coolway.annotation.RequiresLogin)")
    public void afterLogin() {
        System.out.println("结束访问");
    }
}

你可能感兴趣的:(spring,IOC,AOP,spring事务管理)