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对象。
<!-- <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对象。
<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属性初始化
具体实现
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(接口)");}
// 第二种初始化销毁方法//初始化(属性)@PostConstructpublic void doinit() throws Exception {System.out.println("===>生命周期:初始化2(属性)");}//销毁了(属性)@PreDestroypublic 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() {@Overridepublic 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();