Spring DI/IOC核心原理详解

一.SpringDI/IOC


1.SpringDI

DI:(中文依赖注入):是对IOC概念的不同角度的描述。

主要是指应用程序在运行时,每一个bean对象都依赖IOC容器注入当前bean对象所需要的另外一个bean对象。

作用:胶水,帮助springIOC容器,把所有依赖关系的javaBean对象粘合在一起。

实现步骤

1.提供set方法

1.1提供set方法

2.配置标签

2.1set注入

<property 属性名="属性值"></property>

属性:

name=====>属性名

value=====>属性值

ref======>属性值的引用

实现方式

1.set注入

2.构造注入

3.属性注入(不用)

4.注解注入

支持的数据类型

1.基本类型与String

2.JavaBean对象注入(三层调用)

3.复杂类型

具体实现:set注入

复制代码
package com.itheima.service;
import com.itheima.dao.IUserDao;
import com.itheima.dao.UserDaoImp;

public class UserServiceImp implements IUserService {
IUserDao userDao;

    public UserServiceImp(UserDaoImp userDao) {
        this.userDao = userDao;
    }

    public UserServiceImp() {
    }
//    public void setUserDao(IUserDao userDao){
//    this.userDao = userDao;
//}
    public void save() {
        System.out.println("===service的新增===");
        userDao.save();
    }
}
package com.itheima.dao;

public class UserDaoImp implements IUserDao {
    public void save() {
        System.out.println("===dao的新增===");
    }
}
package com.itheima.controller;


import com.itheima.service.IUserService;
import com.itheima.service.UserServiceImp;

public class UserControllerImp implements IUserController {


    IUserService service;

    public UserControllerImp(UserServiceImp service) {
        this.service = service;
    }
    public UserControllerImp() {}

//    public void setService(IUserService service){
//        this.service = service;
//    }
    public void save() {
        System.out.println("===controller的新增===");
        service.save();
    }
}


<!--1.set注入-->
<!--    <bean id="dao" class="com.itheima.dao.UserDaoImp"></bean>-->
<!--    <bean id="service" class="com.itheima.service.UserServiceImp">-->
<!--        <property name="userDao" ref="dao"></property>-->
<!--    </bean>-->
<!--    <bean id="controller" class="com.itheima.controller.UserControllerImp">-->
<!--        <property name="service" ref="service"></property>-->
<!--    </bean>-->

构造注入

复制代码
<!--  构造注入      -->
<!--    <bean id="dao1" class="com.itheima.dao.UserDaoImp"></bean>-->
<!--    <bean id="service1" class="com.itheima.service.UserServiceImp">-->
<!--        <constructor-arg name="userDao" ref="dao1"></constructor-arg>-->
<!--    </bean>-->
<!--    <bean id="controller1" class="com.itheima.controller.UserControllerImp">-->
<!--        <constructor-arg name="service" ref="service1"></constructor-arg>-->
<!--    </bean>-->
三层代码同上

2.SpingIOC-管理bean

bean的创建

1.通过反射调用类的无参构造方法(默认)。

2.通过指定的工厂,创建bean对象。

Delphi 复制代码
<!--    <bean id="singer1" class="com.itheima.pojo.Singer1" factory-bean="factory" factory-method="createSinger"></bean>-->
<!--    <bean id="factory" class="com.itheima.factory.SingerFactory"></bean>-->
package com.itheima.factory;

import com.itheima.pojo.Singer;
import com.itheima.pojo.Singer1;

public class SingerFactory {
    // 创建对象
    public Singer1 createSinger(){
        System.out.println("执行工厂方法");
        return new Singer1();
    }

}

3.通过作用的指定的静态工厂,创建bean对象。

Delphi 复制代码
    <bean id="singer2" class="com.itheima.factory.SingerStaticFactory" factory-method="createSinger" scope="prototype"></bean>
package com.itheima.factory;

import com.itheima.pojo.Singer;
import com.itheima.pojo.Singer1;

public class SingerStaticFactory {
    // 创建对象
    public static Singer1 createSinger(){
        System.out.println("===>执行static工厂的工厂方法");
        return new Singer1();
    }

}

bean的作用域

含义 :bean的创建方式

语法:<bean scope = ''属性值''></bean>

属性值

singleton单例(默认)

prototype多例

request请求

session会话

bean的生命周期

1.实例化:创建对象

2.初始化

2.1接口初始化

2.2属性初始化

具体实现

Delphi 复制代码
package com.itheima.pojo;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

// initializingBean接口
// DisposableBean接口
@Component
public class Teacher implements InitializingBean, DisposableBean {

    //实例化
    public Teacher() {
        System.out.println("===>生命周期:实例化");
    }

    //初始化(接口)
    public void afterPropertiesSet() throws Exception {
        System.out.println("===>生命周期:初始化1(接口)");
    }

    //销毁了(接口)
    public void destroy() throws Exception {
        System.out.println("===>生命周期:销毁了1(接口)");
    }
// 第二种初始化销毁方法
    //初始化(属性)
    @PostConstruct
    public void doinit() throws Exception {
        System.out.println("===>生命周期:初始化2(属性)");
    }
    //销毁了(属性)
    @PreDestroy
    public void dodestory() throws Exception {
        System.out.println("===>生命周期:销毁了2(属性)");
    }
}
<!--    <bean id="teacher" class="com.itheima.pojo.Teacher" init-method="doinit" destroy-method="dodestory"></bean>-->

3.操作使用

4.销毁了(实现同上)

4.1接口销毁了

4.2属性销毁了

3.Spring的配置(第一种纯xml的忽略)

1.spring2.5前==xml ----第一天案例

2.spring2.5后==xml+annotation ----Spring_Day02

3.spring3.0后==annotation+javaConfig配置类 ----Spring_Day02

注意:spring 2.5后=xml+annotation

目的优化一下代码:

<bean id="" class="" init-method="" destroy-method="" scope="" autowire="">

<property></property>

<constructor-arg></constructor-arg>

</bean>

1.spring配置---第二种------spring2.5后==xml+annotation

注解

注入类

替换:<bean id="" class=""></bean>

位置:类

语法:@Component(value="注入容器中的id,如果省略id为类名且首字母小写,value属性名可以省略")

举例

<bean id="user" class="com.apsourse.包.User"></bean>

||等价于||

@Component

Class User{}

注意:不能单独使用,配合扫描使用

<context:component-scan base-pageage=""><</context:component-scan>>

@Repository=====>注入数据访问层

@Service=======>注入业务层

Controller=======> 注入控制层

以上第三个注解与@Component功能语法一致

注入数据

@ value

含义:注入基本数据与String

替换:<property></property>

修饰:成员变量

语法:@Value("数据内容")

@Value("${动态获取}")

注意:不能单独用,配合加载配置文件标签

<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

@Autowired

语法:@Autowired(required="true-默认,false,是否必须进行装配")

修饰:成员变量

含义:注入javaBean

注意:

1.默认是按照类型装配,如果容器中有多个类型,则会自动切换为名称装配

2.默认是按照类型装配,如果容器中有多个类型,则自动切换为名称装配,若名称也没有与之对应则会报异常NoUniqueBeanDefinitionException

3.默认是按照类型装配,如果容器中没有一个 类可以与之匹配,则会报异常NoSuchBeanDefinitionException

其他注解

@Primary

含义:首选项,当类型冲突的情况下,此注解修饰的类被列为首选

修饰:类

注意:不能单独使用,必须与@Component...联合使用

@Qualifier(value="名称")

含义:按照名称装配

修饰:成员变量

注意:不能单独使用,必须与@Autowired联合使用


@Resource(name="名称")

含义:按照名称装配

修饰:成员变量

注意:单独使用

@Scope

含义:配置类的作用域

修饰:类

注意:不能单独使用,必须与@Component...联合使用

@Scope("prototype")

@Scope("singleton")

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

@Scope(ConfigurableBeanFactory.SCOPE.SINGLETON)

@PostConstruct:初始化,替换init-method

@PreDestroy:销毁,替换destory-method

2.Spring配置---第三种(替代applicationContext.xml)文件------spring2.5后==>xml+annotation

@Configuration

作用:指定当前类是一个配置类

细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写

@ComponentScan

作用:用于通过注解指定spring在创建容器时要扫描的包

替换:<context:component-scan base-package=""></context:component-scan>

@PropertySource

作用:用于指定properties文件的位置

替换:<context:property-placeholder location=""></context:property-placeholder>

@Import

作用:用于导入其他的配置类

属性:value:用汉语指定其他配置类的字节码

例子:@Import(SystemSpringConfig.class)

@Bean

作用:用于把当前方法的返回值作为bean对象存入spring的容器中

属性:name:用于指定bean的id,当不写时候,默认值时当前方法的名称

二.动态代理

复制代码
 * 基于接口的动态代理:
 *  特点:字节码随用随创建,随用随加载
 *  作用:不修改源码的基础上对方法增强
 *  涉及的类:Proxy
 *  提供者:JDK官方
 *  如何创建代理对象:
 *      使用Proxy类中的newProxyInstance方法
 *  创建代理对象的要求:
 *      被代理类最少实现一个接口,如果没有则不能使用
 *  newProxyInstance方法的参数:
 *      ClassLoader:类加载器
 *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
 *      Class[]:字节码数组
 *          它是用于让代理对象和被代理对象有相同方法。固定写法。
 *      InvocationHandler:用于提供增强的代码
 
复制代码
 
  *  Object proxy=====》被代理对象的引用===(蔡徐坤对象)
  *  Method method====》执行的方法========(蔡徐坤唱歌方法)
  *  Object[] args====》执行方法的参数=====(蔡徐坤唱歌方法的参数)
  *  Object===========》执行方法的返回值===(蔡徐坤唱歌方法的返回值)
  * */

 * 作用:执行被代理对象的任何接口方法都会经过该方法
 * 方法参数的含义
 *  proxy   代理对象的引用
 *  method  当前执行的方法
 *  args    当前执行方法所需的参数
 *  Object  和被代理对象方法有相同的返回值
 */       
 /*
        第一个参数:ZhouSeenImpl.class.getClassLoader()
        表示类加载器(ClassLoader),用于加载动态生成的代理类
        通常使用被代理类的类加载器,保证类加载的一致性
        第二个参数:ZhouSeenImpl.class.getInterfaces()
        表示被代理类实现的所有接口数组
        动态代理技术要求必须基于接口实现,代理对象会实现这些接口中定义的方法
        这里会返回ZhouSeenImpl类所实现的所有接口(包括ISinger)
        第三个参数:new InvocationHandler()
        这是一个接口实现,用于定义代理逻辑
        其中的invoke()方法会拦截所有对代理对象方法的调用
        可以在该方法中实现增强逻辑(如日志记录、性能监控、事务控制等),再决定是否调用原始对象的方法
*/
复制代码
com.itheima.cglibtest.ISinger iSinger = (ISinger) Proxy.newProxyInstance(ZhouSeenImpl.class.getClassLoader(), ZhouSeenImpl.class.getInterfaces(), new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj = null;
        if (method.getName().equals("song")) {
            System.out.println("跳一段舞");
            obj = method.invoke(new ZhouSeenImpl(), args);
        }
        if (method.getName().equals("sing")) {
            System.out.println("打一个篮球");
            obj = method.invoke(new ZhouSeenImpl(), args);
        }
        return obj;
    }
});
iSinger.song();
相关推荐
是萝卜干呀3 小时前
IIS 部署 asp.net core 项目时,出现500.19、500.31问题的解决方案
后端·iis·asp.net·hosting bundle
从零开始学习人工智能3 小时前
SpringBoot + Apache Tika:一站式解决文件数据提取难题
spring boot·后端·apache
往事随风去3 小时前
Java 中的 Consumer 与 Supplier 接口
java·面试·响应式编程
北城以北88883 小时前
Java高级编程--XML
xml·java·开发语言·intellij-idea
IT_陈寒3 小时前
Python 3.12 的这5个新特性,让我的代码性能提升了40%!
前端·人工智能·后端
华仔啊3 小时前
别再被 Stream.toMap() 劝退了!3 个真实避坑案例,建议收藏
javascript·后端
夕颜1113 小时前
让 Cursor 教我写插件
后端
SXJR3 小时前
Java mp4parser 实现视频mp4 切割
java·python·音视频
冬天vs不冷3 小时前
Java基础(十一):关键字final详解
java·开发语言