spring框架

IOC

Spring是一个容器,这个容器里边放的是对象 ,也就是对象工厂,也就是beans,在spring的思想中IOC(控制反转)AOP(切面编程)是其中的重要部分,对象我们通过applicationContext.xml的配置文件进行配置,bean,也就是创建了对象 实现控制反转,而实现控制反转的方法是DI依赖注入(依赖容器生成对象(通过无参构造方法,有参的话指定name参数属性),通过properties进行 注入)

<bean id="对象名" class="全限定类名(包名+类名)">
    <properties name="属性名" value="具体的属性值"/>
    <array>
        <value></value>
        <value></value>
    </array>//针对数组进行注入
    <list>集合注入
</bean>
# 通过 new ClassPathXml...().getBean()去获取对象    

注解说明

上述是通过配置文件进行注入的,在这里接触一种最为常用的方法,就是注解开发,首先注解开发要在配置文件中加入注解驱动组件扫描 Context:componet-scan然后我们就可以使用注解,

装配注解

-@Autowired:自动进行装配对象的属性等,通过byType,byName,类型和名字
-@Qualifier(value="xxx"),如果有多个对象重名,可以通过这个注解指定唯一的一个
-@Nullabe:如果字段上有这个注解,表示这个字段可以为空
-@Resource: 同样是自动装配,java的,它通过名字、类型进行装配
-@Componet:这是一个spring的组件,放在类之上,相当于<bean id="" class=""/>,放入容器中,就是说明这个类被spring接管了bean,对象的名字跟类名相同
-@value: 上边的组件相当于创建了对象,这个注解放在属性之上,相当于<properties/>,为属性注入了一个值,当然类类型的需要@Autowired,如果属性偏多,那么配置文件更快捷
-@Componet的衍生注解,由于我们在Web开发之中,会按照mvc三层的架构分层,衍生注解的功能和本身是一致的,只是名字不同
    dao层 --@Repository
    Service层--@Service
    controller层--@Controller
-@Scope(value="singletn“)//这个注解设置作用域,可以单例(一个类生成一个对象,多个调用),可以多例(protoType每次调用都会生成一个对象)*/
    XMl与注解
        xml:更加万能,适用于各种场合,维护简单方便
        注解:不是自己的类用不了 维护相对复杂
    XMl与注解的最佳实践
        就是xml负责生成对象,由注解去完成属性注入,sping的项目会使用配置文件和注解,但是springboot全是注解。*/

使用java的方式配置Spring

我们现在完全不使用Spring的xml配置啦,全权交给java来做,javaConfig是spring的一个子项目,在spring4之后,它成为了一个核心功能。 

@Configuration 
public class MyConfig{ 
    @Bean //就相当于以前写的一样 就是注册一个bean 
    public User getUser(){
         return new User; 
     } 
}

@Bean 单独使用,不会注入ioc容器,配合@Componet使用,会注入容器,但是每次都是新创建的对象,配合@Configuration使用,每次从ioc容器中取对象。

@Configuration与@Componet的区别

  • @Component :通用的注解,可标注任意类为 Spring 的组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。

  • @Configuration :声明该类为一个配置类,可以在此类中声明一个或多个 @Bean 方法。

    这两个注解都是配置类注解,作用于类上,申明该类为组件。不同之处在于:

    • @Component是一个元注解,可以注解其他类注解。@Configuration注解里面也是被@Component注解修饰的

    • 返回bean实例不同

    @Configuration 注解修饰的类,并且该注解中的 proxyBeanMethods 属性的值为 true(默认的),则会使用cglib动态代理,为这个 bean 创建一个代理类,该代理类会拦截所有被 @Bean 修饰的方法,在拦截的方法逻辑中,会从容器中返回所需要的单例对象。 @Component 注解修饰的类,则不会为这个 bean 创建一个代理类。 那么我们就会直接执行用户的方法,所以每次都会返回一个新的对象。

DI

DI(Dependency Injection)即依赖注入,对象之间的依赖由容器在运行期决定,即容器动态的将某个依赖注入到对象自身

1、构造方法注入

public class Student {
    private Integer id;
    private String name;

    public Student(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

}

配置文件中赋值

<!--通过有参构造注入依赖-->
    <bean id="student3" class="com.test.pojo.Student">
        <!--id属性注入-->
        <constructor-arg name="id" value="11"/>
        <!--name属性注入-->
        <constructor-arg name="name" value="图论"/>
    </bean>

2、set注入

public class Student {
    private Integer id;
    private String name;
    
    public void setId(Integer id) {this.id = id;}
    public void setName(String name) {this.name = name;}
}

配置文件中赋值

<!--通过setter方法注入依赖-->
    <bean id="student4" class="com.test.pojo.Student"> 
        <property name="id" value="12"/>
        <property name="name" value="Java31"/>
    </bean>

3、自定义类型


    <!--注入自定义类型-->
    <bean id="user" class="com.test.pojo.User">
        <constructor-arg name="name" value="Java"/>
    </bean>
    <bean id="student5" class="com.test.pojo.Student">
        <property name="id" value="12"/>
        <property name="name" value="Java31"/>
        <!--value属性:将参数按照Syringe类型类解析  ref类型:Spring中管理的对象的Id值-->
        <property name="user" ref="user"/>
    </bean>

基于注解注入

  • @Value :注入普通类型属性
  • @Resource :注入对象类型,Java 提供的,默认按照名称来查找并注入类,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
  • @Autowired :注入对象类型,是Spring框架提供的,按照类型来注入,默认情况下必须要求依赖对象存在,如果要允许null值,可以设置它的required属性false。如果想使用名称装配可以结合@Qualifier注解进行使用。

AOP

代理相当于中介的概念,就是帮助别人做一些事情,为什么要学习代理模式,因为这就是springAOP的底层 代理模式的分类:

  • 静态代理
  • 动态代理

静态代理(租房子中介 房东和中介都有租房的需求)

抽象角色:一般会使用接口和抽象类来解决

真实角色:被代理的角色; 代理角色:代理真实角色,代理真实角色之后会做一些附属操作 客户:访问代理对象的人 代理模式的好处 可以让真实角色更加的纯粹,不用去关注一些公共的业务,公共也就交给代理角色,主要是通过代理的附加功能,做一些自己没有做的事情。 缺点,一个真实角色就会产生一个代理角色,代码量翻倍

动态代理

动态代理的底层是反射,动态代理类是自动生成的,不是我们直接写好的 动态代理分为两大类,基于接口的动态代理,基于类的动态代理 基于接口---JDK动态代理 {我们在这里使用} 基于类----cglib java字节码的实现javasist 需要了解两个类 proxy 代理 InvocationHandler 调动处理程序,不需要写代理类,自动生成

什么是AOP

面向切面编程,动态的去修改加强程序。横切关注点,可以做一些日志、切面、事物等。使用的过程中加入织入的依赖。动态代理代理的是接口,要接口 a =new 实现类;

第一种方式:使用spring-api的原生接口

配置

<aop:config>
    //切入点,expression是表达式,第一个*,返回类型,下的包下的所有方法,参数也是所有参数(..)可以定义多个切入点
    <aop:pointcut id="pointcut",expression="execution(* com.shilei.controlller.userServiceimpl.*(..))"/>
    <aop:pointcut id="pointcut1",expression="execution(* com.shilei.controlller.userServiceimpl.*(..))"/>
    //执行环绕增强
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>

第二种方式,自定义类

public class PointCut {
    public void before(){
        System.out.println("====方法执行前=======");
    }
    public void after(){
        System.out.println("=====方法执行后=======");
    }
}
<bean id="pointCut" class="com.test.PointCut"/>
<bean id="userService" class="com.test.UserServiceImpl"/>
<aop:config>
    <!--自定义切面,ref为要引入的类-->
    <aop:aspect ref="pointCut">
        <!--切入点-->
        <aop:pointcut id="point"
                      expression="execution(* com.test.UserServiceImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

第三种方式 :注解实现AOP

package com.web.aop;

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

@Aspect
@Component
public class Log {
    @Before("execution(* com.web.pojo.Service.*(..))")
    public void before() {
        System.out.println("============== Before Method =============");
    }

    @After("execution(* com.web.pojo.Service.*(..))")
    public void after() {
        System.out.println("============== After  Method =============");
    }
}

spring循环依赖问题

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A,属性循环依赖,构造器循环依赖,

检测循环依赖

检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了

解决循环依赖

Spring解决循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的属性是可以延后设置的(但是构造器必须是在获取引用之前)。 Spring的单例对象的初始化主要分为三步:

  • 实例化:其实也就是调用对象的构造方法实例化对象

  • 注入:填充属性,这一步主要是对bean的依赖属性进行填充

  • 初始化:属性注入后,执行自定义初始化

循环依赖主要发生在第一、第二步。也就是构造器循环依赖和属性循环依赖

那么我们要解决循环引用也应该从bean初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

获取单例对象的三级缓存

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 一集缓存中获取
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 二级缓存中获取
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    // 三级缓存中获取
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        //获取到对象加入二级缓存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 在三级缓存中删除
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

调用createBeanInstance实例化后,如果bean是单例,且允许从singletonFactories获取bean,并且当前bean正在创建中,那么就把beanName放入三级缓存(singletonFactories)中,实例化后还没进行属性设置和初始化,但是如果正在创建中,则提前暴露 加入三级缓存

总结一句话 ,先暴露引用,不设置属性,先形成引用关系,属性从缓存中取。

A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象

循环依赖的场景

  • 单例setter注入 能解决

  • 多例setter注入不能解决

  • 构造器注入 不能解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值