Spring系列(1-10)
一、IOC、DI
1、IOC控制反转
2、DI依赖注入
二、Spring容器
1、IOC容器
2、Bean概念
3、Spring容器对象
3.1、BeanFactory接口
//按bean的id或者别名查找容器中的bean Object getBean(String name) throws BeansException //这个是一个泛型方法,按照bean的id或者别名查找指定类型的bean,返回指定类型的bean对象 <T> T getBean(String name, Class<T> requiredType) throws BeansException; //返回容器中指定类型的bean对象 <T> T getBean(Class<T> requiredType) throws BeansException; //获取指定类型bean对象的获取器 <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
3.2、ApplicationContext接口
3.3、ClassPathXmlApplicationContext类
3.4、AnnotationConfigApplicationContext类
三、xml中bean定义
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <import resource="引入其他bean xml配置文件" /> <bean id="bean标识" class="完整类型名称"/> <alias name="bean标识" alias="别名" /> </beans>
1、bean元素
<bean id="bean唯一标识" name="bean名称" class="完整类型名称" factory-bean="工厂bean名称" factory-method="工厂方法"/>
bean名称别名定义规则
当id存在的时候,不管name有没有,取id为bean的名称。
当id不存在,此时需要看name,name的值可以通过","或者 空格 分割,最后会按照分隔符得到一个String数组,数组的第一个元素作为bean的名称,其他的作为bean的别名。
当id和name都存在的时候,id为bean名称,name用来定义多个别名。
当id和name都不指定的时候,bean名称自动生成,生成规则下面详细说明。
bean的class的完整类名#编号 例如:beanName:com.javacode2018.lesson001.demo2.UserModel#0 别名:com.javacode2018.lesson001.demo2.UserModel
2、alias元素
<alias name="需要定义别名的bean" alias="别名" />
3、import元素
<import resource="其他配置文件的位置" />
四、IOC容器创建bean实例的多种方式
1、通过反射调用构造方法创建bean对象
index:构造方法中参数的位置,从0开始,依次递增。
value:指定参数的值。
ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称。
<bean id="bean名称" name="bean名称或者别名" class="bean的完整类型名称"> <constructor-arg index="0" value="bean的值" ref="引用的bean名称" /> <constructor-arg index="1" value="bean的值" ref="引用的bean名称" /> .... <constructor-arg index="n" value="bean的值" ref="引用的bean名称" /> </bean>
2、通过静态工厂方法创建bean对象
<bean id="bean名称" name="" class="静态工厂完整类名" factory-method="静态工厂的方法"> <constructor-arg index="0" value="bean的值" ref="引用的bean名称" /> <constructor-arg index="1" value="bean的值" ref="引用的bean名称" /> .... <constructor-arg index="n" value="bean的值" ref="引用的bean名称" /> </bean>
3、通过实例工厂方法创建bean对象
<bean id="bean名称" factory-bean="需要调用的实例对象bean名称" factory-method="bean对象中的方法"> <constructor-arg index="0" value="bean的值" ref="引用的bean名称" /> <constructor-arg index="1" value="bean的值" ref="引用的bean名称" /> .... <constructor-arg index="n" value="bean的值" ref="引用的bean名称" /> </bean>
4、通过FactoryBean来创建bean对象
<bean id="bean名称" class="FactoryBean接口实现类" />
public class UserFactoryBean implements FactoryBean<UserModel> {
int count = 1;
@Nullable
@Override
public UserModel getObject() throws Exception {
// 返回了一个创建好的UserModel对象
UserModel userModel = new UserModel();
userModel.setName("我是通过FactoryBean创建的第"+count+++ "对象");//@4
return userModel;
}
@Nullable
@Override
public Class<?> getObjectType() {
return UserModel.class;
}
@Override
public boolean isSingleton() {
// 是否是单例
return true;
}
}五、Bean的作用域scope
<bean id="" class="" scope="作用域" />
singleton:单例
prototype:多例
Spring web环境中的作用域
request:在一次http请求中。
session:一次会话中。
application:一个web环境中。
1、自定义Scope
public interface Scope {
/**
* 返回当前作用域中name对应的bean对象
* name:需要检索的bean的名称
* objectFactory:如果name对应的bean在当前作用域中没有找到,那么可以调用这个ObjectFactory来创建这个对象
**/
Object get(String name, ObjectFactory<?> objectFactory);
/**
* 将name对应的bean从当前作用域中移除
**/
@Nullable
Object remove(String name);
/**
* 用于注册销毁回调,如果想要销毁相应的对象,则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象
*/
void registerDestructionCallback(String name, Runnable callback);
/**
* 用于解析相应的上下文数据,比如request作用域将返回request中的属性。
*/
@Nullable
Object resolveContextualObject(String key);
/**
* 作用域的会话标识,比如session作用域将是sessionId
*/
@Nullable
String getConversationId();
}定义了作用域的名称为一个常量thread,可以在定义bean的时候给scope使用
/**
* 自定义本地线程级别的bean作用域,不同的线程中对应的bean实例是不同的,同一个线程中同名的bean是同一个实例
*/
public class ThreadScope implements Scope {
public static final String THREAD_SCOPE = "thread";
private ThreadLocal<Map<String, Object>> beanMap = new ThreadLocal() {
@Override
protected Object initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object bean = beanMap.get().get(name);
if (Objects.isNull(bean)) {
bean = objectFactory.getObject();
beanMap.get().put(name, bean);
}
return bean;
}
@Override
public Object remove(String name) {
return this.beanMap.get().remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
//bean作用域范围结束的时候调用的方法,用于bean清理
System.out.println(name);
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}第2步:使用自定义的作用域
<!-- 自定义scope的bean --> <bean id="threadBean" class="com.javacode2018.lesson001.demo4.BeanScopeModel" scope="thread"> <constructor-arg index="0" value="thread"/> </bean>
/** * 向容器中注册自定义的Scope *scopeName:作用域名称 * scope:作用域对象 **/ void registerScope(String scopeName, Scope scope);
public static void main(String[] args) throws InterruptedException {
String beanXml = "classpath:/com/javacode2018/lesson001/demo4/beans/thread.xml";
//手动创建容器
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext(){
@Override
protected void
postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//向容器中注册自定义的scope
beanFactory.registerScope(ThreadScope.THREAD_SCOPE, new
ThreadScope());
super.postProcessBeanFactory(beanFactory);
}
};
//设置配置文件位置
context.setConfigLocation(beanXml);
//启动容器
context.refresh();
//使用容器获取bean
for (int i = 0; i < 2; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread() + "," +
context.getBean("threadBean"));
System.out.println(Thread.currentThread() + "," +
context.getBean("threadBean"));
}).start();
TimeUnit.SECONDS.sleep(1);
}
}六、依赖注入之手动注入
1、通过构造器注入
1.1、根据构造器参数索引注入
<bean id="diByConstructorParamIndex" class="com.javacode2018.lesson001.demo5.UserModel"> <constructor-arg index="0" value="路人甲Java"/> <constructor-arg index="1" value="上海市"/> </bean>
1.2、根据构造器参数类型注入
<bean id="diByConstructorParamType" class="com.javacode2018.lesson001.demo5.UserModel"> <constructor-arg type="参数类型" value="参数值"/> <constructor-arg type="参数类型" value="参数值"/> </bean>
1.3、根据构造器参数名称注入
<bean id="diByConstructorParamName" class="com.javacode2018.lesson001.demo5.UserModel"> <constructor-arg name="参数类型" value="参数值"/> <constructor-arg name="参数类型" value="参数值"/> </bean>
java通过反射的方式可以获取到方法的参数名称,不过源码中的参数通过编译之后会变成class对象,通常情况下源码变成class文件之后,参数的真实名称会丢失,参数的名称会变成arg0,arg1,arg2这样的,和实际参数名称不一样了,如果需要将源码中的参数名称保留在编译之后的class文件中,编译的时候需要用下面的命令:
javac -parameters java源码
编译的时候不可能手动编译,Spring提供了解决方案,通过ConstructorProperties注解来定义参数的名称,将这个注解加在构造方法上面,如下:
@ConstructorProperties({"第一个参数名称", "第二个参数的名称",..."第n个参数的名称"})
public 类名(String p1, String p2...,参数n)2、setter注入
<bean id="" class=""> <property name="属性名称" value="属性值" /> ... <property name="属性名称" value="属性值" /> </bean>
3、注入容器中的bean
3.1、ref属性方式
<constructor-arg ref="需要注入的bean的名称"/>
setter方式,将value替换为ref:
<property name="属性名称" ref="需要注入的bean的名称" />
3.2、内置bean的方式
<constructor-arg> <bean class=""/> </constructor-arg
setter方式:
<property name="属性名称"> <bean class=""/> </property>
4、其他类型注入
public class DiOtherTypeModel {
private List<String> list1;
private Set<UserModel> set1;
private Map<String, Integer> map1;
private int[] array1;
private Properties properties1;
public List<String> getList1() {
return list1;
}
public void setList1(List<String> list1) {
this.list1 = list1;
}
public Set<UserModel> getSet1() {
return set1;
}
public void setSet1(Set<UserModel> set1) {
this.set1 = set1;
}
public Map<String, Integer> getMap1() {
return map1;
}
public void setMap1(Map<String, Integer> map1) {
this.map1 = map1;
}
public int[] getArray1() {
return array1;
}
public void setArray1(int[] array1) {
this.array1 = array1;
}
public Properties getProperties1() {
return properties1;
}
public void setProperties1(Properties properties1) {
this.properties1 = properties1;
}
@Override
public String toString() {
return "DiOtherTypeModel{" +
"list1=" + list1 +
", set1=" + set1 +
", map1=" + map1 +
", array1=" + Arrays.toString(array1) +
", properties1=" + properties1 +
'}';
}
}<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="user1" class="com.javacode2018.lesson001.demo5.UserModel"/> <bean id="user2" class="com.javacode2018.lesson001.demo5.UserModel"/> <bean id="diOtherType" class="com.javacode2018.lesson001.demo5.DiOtherTypeModel"> <!-- 注入java.util.List对象 --> <property name="list1"> <list> <value>Spring</value> <value>SpringBoot</value> </list> </property> <!-- 注入java.util.Set对象 --> <property name="set1"> <set> <ref bean="user1"/> <ref bean="user2"/> <ref bean="user1"/> </set> </property> <!-- 注入java.util.Map对象 --> <property name="map1"> <map> <entry key="路人甲Java" value="30"/> <entry key="路人" value="28"/> </map> </property> <!-- 注入数组对象 --> <property name="array1"> <array> <value>10</value> <value>9</value> <value>8</value> </array> </property> <!-- 注入java.util.Properties对象 --> <property name="properties1"> <props> <prop key="key1">java高并发系列</prop> <prop key="key2">mybatis系列</prop> <prop key="key3">mysql系列</prop> </props> </property> </bean> </beans>
七、依赖注入之自动注入(autowire)
<bean id="" class="" autowire="byType|byName|constructor|default" />
byteName:按照名称进行注入
byType:按类型进行注入
constructor:按照构造方法进行注入
default:默认注入方式
八、通过depend-on干预bean创建和销毁顺序
<bean id="bean1" class="" depend-on="bean2,bean3,bean4" />
九、primary=true优先注入
<bean id="bean1" class="" primary="true" />
十、autowire-candidate是否作为候选bean
<bean id="bean1" class="" autowire-candidate="false"/>
参考:路人甲-Spring系列上。
版权声明
非特殊说明,本文由Zender原创或收集发布,欢迎转载。
ZENDER
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。