1.什么是ioc控制反转 ?
传统代码 ,耦合度太高,调其他类的方法需要new对象,现在将创建对象(通过配置文件)管理都交给beanFactory 来管理,开发人员用的时候只需要直接拿就行了。
反转的时bean的创建权。
2. 什么是di依赖注入?
beanFactory工厂 对象和对象之间的引用,比如在表述层有个control类需要掉用业务层的impl类,原来是得在control类里面new impl类,但是由于两个对象control
和impl都由beanfactory工厂管理创建,所以beanfactory工厂在创建control类的时候就会把impl对象赋值给control的一个属性,这个过程叫注入。不注入对象注入普通参数也是。
通过set方法
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"> <!--控制反转-->
<constructor-arg name="name" value="张三"></constructor-arg> <!--通过有参构造创建bean对象 默认是通过无参构造-->
<property name="userDao" ref="userDao"></property> <!--依赖注入-->
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
3.applicationContext 叫ioc容器
早期只有beanfactory工厂 bean对象都由他创建, 现在有他的子接口applicationContext 叫ioc容器 比他更强大。
beanfactory是懒加载 只有在getbean的时候(使用这个对象的时候) 才加载对象 。
applicationContext在读取xml配置文件(项目启动)的时候就已经加载了。
4.bean标签
1.id:
//唯一不重复 如果不写 那么id就是全限定类名 每次getBean的时候 其实都是通过 name获取的 只不过 自动把id 转换成name了
2.class:
//要配置的生成对象的全限定类名
3.name:
//起一个别名 可以按,分开其多个名字name=“aaa,bbb,ccc”
4.scop:
//对象的作用范围,就是生成的对象放在什么位置。
1. singleton:
//单例,不写默认就是这个,spring容器创建的时候就会生成,用完再放回去。
疑问? 项目部署的时候多线程访问一个项目 要用同一个 对象 那么在beanFactory共工厂里面还是只有一个吗?
回答:是,他们都用同一个实例对象,所以设计的时候要设计成无状态实例它们可以同时执行任务,不会相互阻塞。
2.prototype:
//多例,只有在getbean的时候才会创建对象,用完直接销毁。所以每次getBean的地址都不一样。
拓展://如果导入了spring-webmvc 那么还会有两种 requet和session 和第一种一样 也只有一个 不过存的位置不一样
5.lazy-init:
//是否开启延迟加载 只对applicationContext有效 可以让他 getBean的时候在创建bean
6.init-method:
//指定初始化方法 创建对象后执行 对象中的 自定义方法
注意:实现initializingBean接口重写afterpropertiesSet()方法 会比init-method配置的方法还要早执行,
基本执行顺序 创建对象=》给属性赋值=》afterpropertiesSet()=》init-method()》destory-method
7.destory-method:
//指定销毁方法 容器 关闭前执行
8.factory-method:
//工厂方式创建对象,此时不会调用构造而是,调用自定义的静态方法(这个方法 new 对象 然后return回来)创建对象,
好处1:可以在创建之前做其他的操作,(毕竟方法是自己写的)。
好处2:一些不是自己定义的bean如第三方jar包里面的bean也需要交给spring管理,就是通过这种方式。(springboot存疑?)
注意工厂模式 产生的实例对象是 方法return 的返回值
1.静态工厂
<!--静态工厂方法方式 由于工厂是静态的不需要实例化-->
<bean id="userDao1" class="com.itheima.factory.MyBeanFactory1" factory-method="userDao">
<constructor-arg name="name" value="haohao"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
</bean>
public class MyBeanFactory1 {
public static UserDao userDao(String name,int age){
//Bean创建之前可以进行一些其他业务逻辑操作
return new UserDaoImpl();
}
}
2.非静态工厂
<!--非静态工厂 需要先配置工厂 实例化工厂 -->
<bean id="myBeanFactory2" class="com.itheima.factory.MyBeanFactory2"></bean>
<bean id="userDao2" factory-bean="myBeanFactory2" factory-method="userDao">
<constructor-arg name="username" value="haohao"/>
</bean>
public class MyBeanFactory2 {
public UserDao userDao(String username){
//Bean创建之前可以进行一些其他业务逻辑操作
return new UserDaoImpl();
}
}
3.通过fatcoryBean spring提供的接口
这种方式举有延迟实例化对象功能,只有getBean(获取用到对象)的时候才会 实例化对象 并把对象 放在beanFactory的缓存中。
<bean id="userDao3" class="com.itheima.factory.MyBeanFactory3"></bean>
public class MyBeanFactory3 implements FactoryBean<UserDao> {
@Override
public UserDao getObject() throws Exception {
System.out.println("getObject被调用...");
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
9.注入方式
10.自动装配 autowire
如果一个标签的id和另一个标签的name一样,Spring 会优先选择 name
匹配的 Bean。
11.xml文件 命名空间引用
一个主配置文件 引入其他配置文件 使其生效
<import resource="classpath:UserModuleApplicationContext.xml"></import>
12.springbean实例化流程
spring容器在进行初始化时,会将xml配置的的信息封装成一个BeanDefinition对象,所有的 BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反 射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用getBean方法 时则最终从该Map集合中取出Bean实例对象返回。
⚫ 加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;
⚫ 将BeanDefinition存储在一个名为beanDefinitionMap的Map中;
⚫ ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
⚫ 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;
⚫ 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回
13.spring后置处理器
1.BeanFactoryPostProcessor:‘
Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;只会执行一次
<bean class="com.itheima.processor.MyBeanFactoryPostProcessor"></bean>
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor的postProcessBeanFactory");
//System.out.println("beanDefinitionMap填充完毕后回调该方法");
//注册BeanDefinition 使用后置处理把自定义的bean 注册到map
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.itheima.dao.impl.PersonDaoImpl");
//强转成DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition("personDao",beanDefinition);
//修改某个Beandifinition
BeanDefinition beanDefinition2 = beanFactory.getBeanDefinition("userService");
beanDefinition2.setBeanClassName("com.itheima.dao.impl.UserDaoImpl");
}
}
1.自定义注解 装配bean 练习通过后置处理 和
@MyComponent("otherBean")
public class OtherBean {
}
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//通过扫描工具去扫描指定包及其子包下的所有类,收集使用@Mycomponent的注解的类
Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.itheima");
//遍历Map,组装BeanDefinition进行注册
myComponentAnnotationMap.forEach((beanName,clazz)->{
//获得beanClassName
String beanClassName = clazz.getName();//com.itheima.beans.OtherBean
//创建BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
//注册
beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
2.BeanPostProcessor:
Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。每实例化一个执行一次
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
/*if(bean instanceof UserDaoImpl){
UserDaoImpl userDao = (UserDaoImpl) bean;
userDao.setUsername("haohao");
}*/
System.out.println(beanName+":postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessAfterInitialization");
return bean;
}
}
1.在循环放入 signlObjects 前 给对象增强(动态代理 )
使执行这个对象的任何方法 前后都打印执行时间
public class TimeLogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//使用动态代理对目标Bean进行增强,返回proxy对象,进而存储到单例池singletonObjects中
Object beanProxy = Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(proxy, method, args) -> {
//1、输出开始时间
System.out.println("方法:" + method.getName() + "-开始时间:" + new Date());
//2、执行目标方法
Object result = method.invoke(bean, args);
//3、输出结束时间
System.out.println("方法:" + method.getName() + "-结束时间:" + new Date());
return result;
}
);
return beanProxy;
}
}
14.springBean 生命周期
2.springbean的初始化阶段
15.循环依赖问题
有A,B两个 对象,A中属性需要B注入,B中属性需要A注入,spring首先实例化A开辟内存空间,然后注入属性发现B还没有实例,就会先去实例化B,B需要注入A,怎么办?
当一个对象 需要注入另一个对象时,会先去一级缓存signltonObjects(最终单例池)里面去找,没找到会去,二级缓存earlySingletonObjects(被引用过的半成品bean)找,还没有就去三级缓存singletonFactories(实例化对象还没有注入属性的半成品bean)找。
16.常用的Aware接口
可以让一个普通bean 获取 到框架中的对象 比如 不是表述层的类 获取到 servletContext
实现这个四个接口重写对应的方法 ,spring在生成对象时候 会自动执行这四个方法,就可以获取到 servletContext BeanFactory beanName ApplaicationContext 这四个对象,然后用属性接收一下就可以。
17.完整周期图
18.spring整合mybatis
不需要mybatis-config.xml文件了
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!--配置数据源信息-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--MapperScannerConfigurer,作用扫描指定的包(dao层的接口还有对应的配置文件,放到beanfactory工厂),产生Mapper对象存储到Spring容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.mapper"></property>
</bean>
<!--<bean class="com.itheima.processor.TimeLogBeanPostProcessor"></bean>-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"></property>
</bean>
19.spring注解开发
<!--注解组件扫描:扫描指定的基本包及其子包下的类,识别使用@Component注解-->
<context:component-scan base-package="com.itheima"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
读取配置文件 放到spring容器
@Autowared会先根据类型匹配如果有多个会根据name在匹配
@Bean 注入 非自定义的对象(不能写在类上面)比如第三方框架的对象 想注入 spring容器 可以写在方法上
20.使用配置类替代配置文件
package com.itheima.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.anno.MyMapperScan;
import com.itheima.beans.OtherBean;
import com.itheima.imports.MyImportBeanDefinitionRegistrar;
import com.itheima.imports.MyImportSelector;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration //标注当前类是一个配置类(替代配置文件)+@Component
//<context:component-scan base-package="com.itheima"/>
@ComponentScan("com.itheima")//注解扫描使注解生效
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")//读取配置文件
//<import resource="xxx.xml"></import>
//@Import(OtherBean.class)//导入其他配置类
//@Import({MyImportSelector.class})
//@Import(MyImportBeanDefinitionRegistrar.class)
//Mapper的接口扫描
@MapperScan("com.itheima.mapper")//接口扫描实际上是为mapper(和xml文件)创建为对象放到spring容器中
@MyMapperScan
public class SpringConfig {
@Bean
public DataSource dataSource(
@Value("${jdbc.driver}") String driver,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String username,
@Value("${jdbc.password}") String password
){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
public class ApplicationContextTest {
public static void main(String[] args) {
//System.setProperty("spring.profiles.active","test");
//xml方式的Spring容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//注解方式去加载Spring的核心配置类
//ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
/*UserService userService = applicationContext.getBean(UserService.class);
userService.show();*/
/*Object userDao = applicationContext.getBean("userDao2");
System.out.println(userDao);*/
/*UserService userService = applicationContext.getBean(UserService.class);
userService.show();*/
OtherBean2 bean = applicationContext.getBean(OtherBean2.class);
System.out.println(bean);
}
@Profile
@Profile("test") //设置test环境的时候 才会注入到spring容器
//没有注解的相当于任何环境都注入
public class UserDaoImpl implements UserDao {
@Value("${jdbc.driver}")
private String username;
//@Value("lisi")
public void setUsername(String username){
this.username = username;
}
@Override
public void show() {
//System.out.println(username);
}
21.自定义框架
整合第三方框架 可以直接bean标签配置 ,第三方框架繁琐的的bean可以自定义命名空间,如果要整合注解 需要用import