Spring框架学习笔记(二):Spring IOC容器配置 Bean,分别基于XML配置bean 和 基于注解配置 bean

Spring 配置/管理 bean 介绍

Bean 管理包括两方面 :创建 bean 对象;给 bean 注入属性

Bean 配置方式:基于 xml 文件配置方式;基于注解方式

基于 XML 配置 bean

2.1 通过类型来获取 bean

方法:给getBean传入一个Class对象

示例:

ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster = ioc.getBean(Monster.class);

说明:

(1) 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个 , 否则会抛出异常
NoUniqueBeanDefinitionException
(2)这种方式的应用场景:比如 XxxAction/Servlet/Controller, XxxService 在一个线程
中只需要一个对象实例 ( 单例 ) 的情况
(3)在容器配置文件 ( 比如 beans.xml) 中给属性赋值 , 底层是通过setter 方法完成的 , 这也是为什么我们需要提供 setter 方法的原因

2.2 通过构造器配置 bean

(1)constructor-arg标签可以指定使用构造器的参数

(2)index表示构造器的第几个参数 从0开始计算的

(3)除了可以通过index 还可以通过 name / type 来指定参数方式,name表示参数的名字,type表示参数的类型

(4)类的构造器,不能有完全相同类型和顺序的构造器,所以可以通过type来指定

<bean id="monster03" class="com.spring.bean.Monster"><constructor-arg value="200" index="0"/><constructor-arg value="白骨精" index="1"/><constructor-arg value="吸人血" index="2"/>
</bean><bean id="monster04" class="com.spring.bean.Monster"><constructor-arg value="200" name="monsterId"/><constructor-arg value="白骨精" name="name"/><constructor-arg value="吸人血" name="skill"/>
</bean><bean id="monster05" class="com.spring.bean.Monster"><constructor-arg value="300" type="java.lang.Integer"/><constructor-arg value="白骨精~" type="java.lang.String"/><constructor-arg value="吸人血~" type="java.lang.String"/>
</bean>

说明:

  • 通过 index 属性来区分是第几个参数
  • 通过 type 属性来区分是什么类型(按照参数顺序)

2.3 通过 p 名称空间配置 bean

xml 文件配置, 增加命名空间配置

将光标放在p , 输入alt+enter , 就会自动的添加xmlns

示例:

<!--通过p名称空间来配置bean将光标放在p , 输入alt+enter , 就会自动的添加xmlns
-->
<bean id="monster06" class="com.spring.bean.Monster"p:monsterId="500"p:name="红孩儿"p:skill="吐火"
/>

2.4 引用/注入外部 bean 对象

在 spring 的 ioc 容器, 可以通过 ref 来实现 bean 对象的相互引用

应用案例:

(1) 创建 MemberDAOImpl.java
public class MemberDAOImpl {public MemberDAOImpl() {System.out.println("MemberDAOImpl 构造器...");}public void add() {System.out.println("MemberDAOImpl add()方法");}
}

(2)建 MemberServiceImpl.java

public class MemberServiceImpl {private MemberDAOImpl memberDAO;public MemberServiceImpl() {System.out.println("MemberServiceImpl 构造器~");}public void add() {System.out.println("MemberServiceImpl add()...");memberDAO.add();}public void setMemberDAO(MemberDAOImpl memberDAO) {this.memberDAO = memberDAO;}public MemberDAOImpl getMemberDAO() {return memberDAO;}
}

(3)beans.xml 配置

<!-- bean 对象的相互引用
1. 其它含义和前面一样
2. ref 表示 memberDAO 这个属性将引用/指向 id = memberDAOImpl 对象
-->
<bean id="memberServiceImpl" class="com.spring.service.MemberServiceImpl"><property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.spring.dao.MemberDAOImpl"/>

(5)测试

ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");
MemberServiceImpl bean = (MemberServiceImpl)ioc.getBean("memberServiceImpl", MemberServiceImpl.class);
System.out.println(bean);

细节说明:

  • 这里就体现出spring容器的依赖注入
  • 注意在spring容器中, xml是作为一个整体来执行的, 即如果你引用到一个bean对象, 对你配置的顺序没有要求,即bean的顺序没有要求
  • 建议还是按顺序,好处是阅读的时候,比较方便

2.5 引用/注入内部 bean 对象

spring ioc 容器中, 可以直接在 bean 内部配置内部 依赖 bean 对象

示例:
<!--配置MemberServiceImpl对象-使用内部bean-->
<bean class="com.spring.service.MemberServiceImpl" id="memberService2"><!--自己配置一个内部bean--><property name="memberDAO"><bean class="com.spring.dao.MemberDAOImpl"/></property>
</bean>

2.6 引用/注入集合/数组类型

spring ioc 容器, bean 对象的集合/数组类型属性赋值

应用案例:

(1)创建 Master.java

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;public class Master {private String name;private List<Monster> monsterList;private Map<String, Monster> monsterMap;private Set<Monster> monsterSet;private String[] monsterName;//这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式
//这里 Properties key 和 value 都是 Stringprivate Properties pros;public Master() {}public Master(String name) {this.name = name;}public Set<Monster> getMonsterSet() {return monsterSet;}public void setMonsterSet(Set<Monster> monsterSet) {this.monsterSet = monsterSet;}public String[] getMonsterName() {return monsterName;}public void setMonsterName(String[] monsterName) {this.monsterName = monsterName;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<Monster> getMonsterList() {return monsterList;}public void setMonsterList(List<Monster> monsterList) {this.monsterList = monsterList;}public Map<String, Monster> getMonsterMap() {return monsterMap;}public void setMonsterMap(Map<String, Monster> monsterMap) {this.monsterMap = monsterMap;}public Properties getPros() {return pros;}public void setPros(Properties pros) {this.pros = pros;}
}

(2)配置 beans.xml

<!--配置Master对象
体会 spring 容器配置特点 依赖注入-非常灵活
-->
<bean class="com.spring.bean.Master" id="master"><property name="name" value="太上老君"/><!--给list属性赋值--><property name="monsterList"><list><!--引用的方法--><ref bean="monster01"/><ref bean="monster02"/><!--内部bean--><bean class="com.spring.bean.Monster"><property name="name" value="老鼠精"/><property name="monsterId" value="100"/><property name="skill" value="吃粮食"/></bean></list></property><!--给map属性赋值--><property name="monsterMap"><map><entry><key><value>monster03</value></key><!--这里使用的外部bean,引入--><ref bean="monster03"/></entry><entry><key><value>monster04</value></key><ref bean="monster04"/></entry></map></property><!--给set属性赋值--><property name="monsterSet"><set><ref bean="monster05"/><ref bean="monster06"/><bean class="com.spring.bean.Monster"><property name="name" value="金角大王"/><property name="skill" value="吐水"/><property name="monsterId" value="666"/></bean></set></property><!--给数组属性赋值array标签中使用 value 还是 bean , ref .. 要根据你的业务决定--><property name="monsterName"><array><value>小妖怪</value><value>大妖怪</value><value>老妖怪</value></array></property><!--给Properties属性赋值 结构k(String)-v(String)--><property name="pros"><props><prop key="username">root</prop><prop key="password">123456</prop><prop key="ip">127.0.0.1</prop></props></property>
</bean>

(3)测试

public static void main(String[] args) {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Master master01 = ioc.getBean("master", Master.class);//获取 list 集合System.out.println("======list=======");List<Monster> monster_list = master01.getMonsterList();for (Monster monster : monster_list) {System.out.println(monster);}//获取 map 集合System.out.println("======map=======");Map<String, Monster> monster_map = master01.getMonsterMap();Set<Map.Entry<String, Monster>> entrySet = monster_map.entrySet();for (Map.Entry<String, Monster> entry : entrySet) {System.out.println(entry);}//获取 properties 集合System.out.println("======properties=======");Properties pros = master01.getPros();String property1 = pros.getProperty("username");String property2 = pros.getProperty("password");String property3 = pros.getProperty("ip");System.out.println(property1 + "\t" + property2 + "\t" + property3);//获取数组System.out.println("======数组=======");String[] monsterName = master01.getMonsterName();for (String s : monsterName) {System.out.println("妖怪名= " + s);}//获取 setSystem.out.println("======set=======");Set<Monster> monsterSet = master01.getMonsterSet();for (Monster monster : monsterSet) {System.out.println(monster);}
}

运行效果:

细节说明:

(1)主要掌握 List/Map/Properties 三种集合的使用 .
(2)Properties 集合的特点
  • 这个 Properties Hashtable 的子类 , key-value 的形式
  • key string value 也是 string

2.7 通过 util 名称空间创建 list

spring 的 ioc 容器, 可以通过 util 名称空间创建 list

<!--通过 util 名称空间来创建 list 集合,可以当做创建 bean 对象的工具来使用
-->
<util:list id="myListBook"><value>三国演义</value><value>西游记</value><value>红楼梦</value><value>水浒传</value>
</util:list>
<bean id="bookStore" class="com.spring.bean.BookStore"><property name="bookList" ref="myListBook"/>
</bean>

集合

应用案例:

(1)创建 BookStore.java

public class BookStore {//书店private List<String> bookList;public BookStore() {}public List<String> getBookList() {return bookList;}public void setBookList(List<String> bookList) {this.bookList = bookList;}
}

(2)修改 beans.xml , 增加配置

<!--通过 util 名称空间来创建 list 集合,可以当做创建 bean 对象的工具来使用
-->
<util:list id="myListBook"><value>三国演义</value><value>西游记</value><value>红楼梦</value><value>水浒传</value>
</util:list>
<bean id="bookStore" class="com.spring.bean.BookStore"><property name="bookList" ref="myListBook"/>
</bean>

(3)测试

@Test
public void bookStoreTest() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");BookStore bookStore = ioc.getBean("bookStore", BookStore.class);System.out.println(bookStore.getBookList());
}

2.8 级联属性赋值

spring 的 ioc 容器, 可以直接给对象属性的属性赋值, 即级联属性赋值

应用案例:

(1)创建 Dept.java

public class Dept {private String name;public Dept() {}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

(2)创建 Emp.java

public class Emp {private String name;private Dept dept;public Emp() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public Dept getDept() {return dept;}public void setDept(Dept dept) {this.dept = dept;}
}

(3)修改 beans.xml , 增加配置

<!--配置Dept对象-->
<bean class="com.spring.bean.Dept" id="dept"/>
<!--配置Emp对象-->
<bean class="com.spring.bean.Emp" id="emp"><property name="name" value="jack"/><property name="dept" ref="dept"/><!--给dept的name属性指定值[级联属性赋值]--><property name="dept.name" value="Java开发部门"/>
</bean>

2.9 通过静态工厂获取对象

在 spring ioc 容器, 可以通过静态工厂获取 bean 对象

应用案例:

(1)创建 MyStaticFactory.java

import java.util.HashMap;
import java.util.Map;public class MyStaticFactory {private static Map<String, Monster> monsterMap;static {monsterMap = new HashMap<String, Monster>();monsterMap.put("monster_01", new Monster(100, "黄袍怪", "一阳指"));monsterMap.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));}public static Monster getMonster(String key) {return monsterMap.get(key);}
}

(2)修改 beans.xml , 增加配置

<!-- 通过静态工厂来获取 bean 对象 -->
<bean id="my_monster" class="com.spring.factory.MyStaticFactory"factory-method="getMonster"><!-- constructor-arg 标签提供 key --><constructor-arg value="monster_01"/>
</bean>

(3)测试

@Test
public void test02() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster = ioc.getBean("my_monster", Monster.class);System.out.println(monster);
}

2.10 通过实例工厂获取对象

在 spring ioc 容器, 可以通过实例工厂获取 bean 对象

 应用案例:

(1)建 MyInstanceFactory.java

import java.util.HashMap;
import java.util.Map;public class MyInstanceFactory {private Map<String, Monster> monster_map;//非静态代码块{monster_map = new HashMap<String, Monster>();monster_map.put("monster_01", new Monster(100, "猴子精", "吃人"));monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));}public Monster getMonster(String key) {return monster_map.get(key);}
}

(2)配置 beans.xml

<!-- 通过实例工厂来获取 bean 对象 -->
<bean id="myInstanceFactory" class="com.spring.factory.MyInstanceFactory"/>
<bean id="my_monster2" factory-bean="myInstanceFactory"factory-method="getMonster"><constructor-arg value="monster_02"/>
</bean>

(3)测试代码

@Test
public void getBeanByInstanceFactory() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster my_monster = ioc.getBean("my_monster2", Monster.class);System.out.println(my_monster);
}

(4)结果

2.11 通过 FactoryBean 获取对象

在 spring ioc 容器,通过 FactoryBean 获取 bean 对象

应用案例:

(1)创建MyFactoryBean.java

说明:

  • 该类实现了 FactoryBean 接口,并指定了泛型Monster
  • 创建了一个map集合,用于存放创建好的对象
  • 重写了getObject方法,返回keyVal对应的对象,即this.monster_map.get(keyVal)
  • 重写了getObjectType方法,返回对象的运行类型
  • 重写isSingleton方法,表示对象是否为单例,true为是
public class MyFactoryBean implements FactoryBean<Monster> {private String keyVal;private Map<String, Monster> monster_map;{monster_map = new HashMap<String, Monster>();monster_map.put("monster_01", new Monster(100, "黄袍怪", "一阳指"));monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));}public void setKeyVal(String keyVal) {this.keyVal = keyVal;}@Overridepublic Monster getObject() throws Exception {
// TODO Auto-generated method stubreturn this.monster_map.get(keyVal);}@Overridepublic Class getObjectType() {
// TODO Auto-generated method stubreturn Monster.class;}@Overridepublic boolean isSingleton() {
// TODO Auto-generated method stubreturn true;}
}

(2)配置 beans.xml

<!--1. 通过 FactoryBean 来获取 bean 对象2. name="keyVal" 就是 MyFactoryBean 定义的 setKeyVal 方法3. value="monster_01" ,就是给 keyVal 的值
-->
<bean id="myFactoryBean" class="com.spring.factory.MyFactoryBean"><property name="keyVal" value="monster_01"/>
</bean>

(3)测试代码

@Test
public void getBeanByFactoryBean() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster = ioc.getBean("myFactoryBean", Monster.class);System.out.println(monster);
}

2.12 bean 配置信息重用(继承)

在 spring ioc 容器中, 提供了一种继承的方式来实现 bean 配置信息的重用

应用案例:

(1)配置beans.xml

<!-- 继承的方式来实现 bean 配置信息的重用 -->
<bean id="monster10" class="com.spring.bean.Monster"><property name="monsterId" value="10"/><property name="name" value="蜈蚣精"/><property name="skill" value="蜇人"/>
</bean>
<!-- parent="monster10" 就是继承使用了 monster10 的配置信息 -->
<bean id="monster11" class="com.spring.bean.Monster" parent="monster10"/>
<!-- 当我们把某个bean设置为 abstract="true" 这个bean只能被继承,而不能实例化了 -->
<bean id="monster12" class="com.spring.bean.Monster" abstract="true"><property name="monsterId" value="12"/><property name="name" value="美女蛇"/><property name="skill" value="吃人"/>
</bean>
<!-- parent="monster12" 就是继承使用了 monster12 的配置信息 -->
<bean id="monster13" class="com.spring.bean.Monster" parent="monster12"/>

说明:

  • 可通过parent="monster10"方式继承其他bean,实现bean 配置信息的重用
  • 把某个bean设置为 abstract="true" ,这个bean只能被继承,而不能实例化了

测试代码:

@Test
public void getBeanByExtends() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster1 = ioc.getBean("monster11", Monster.class);System.out.println(monster1);Monster monster2 = ioc.getBean("monster13", Monster.class);System.out.println(monster2);
}

2.13 bean 创建顺序

说明:

(1)在 spring ioc 容器, 默认是按照配置的顺序创建 bean 对象

<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />

会先创建 student01 这个 bean 对象,然后创建 department01 这个 bean 对象

(2)如果这样配置,表示 student01 对象依赖于 department01 对象

<bean id="student01" class="com.hspedu.bean.Student" depends-on="department01"/>
<bean id="department01" class="com.hspedu.bean.Department" />

就会先创建 department01 对象,再创建 student01 对象.

(3)如果使用ref关联两个bean,无需关注配置顺序

说明:spring会先把有关联的对象都创建好,再处理引用关系

案例:

1. 先看下面的配置 , 两个 bean 创建的顺序如下
  • 先创建 id=memberDAOImpl
  • 再创建 id = memberServiceImpl
  • 调用 memberServiceImpl.setMemberDAO() 完成引用

2. 先看下面的配置 , 请问两个 bean 创建的顺序如下
  • 先创建 id = memberServiceImpl
  • 再创建 id=memberDAOImpl
  • 最后调用 memberServiceImpl.setMemberDAO() 完成引用

2.14 bean 对象的单例和多例

在 spring 的 ioc 容器, 默认是按照单例创建的,即配置一个 bean 对象后,ioc 容器只会创建一个 bean 实例。

如果,我们希望 ioc 容器配置的某个 bean 对象,是以多个实例形式创建的则可以通过配置 scope="prototype" 来指定,这样spring在获取对象时才会临时创建该对象,而不是像单例对象一样提前创建好

应用案例:

(1)创建 Car.java

public class cat {public cat() {System.out.println("cat 构造器");}
}

(2)配置 beans.xml

<!-- 
如果希望 ioc 容器配置的某个 bean 对象,
是以多个实例形式创建可以通过配置 scope="prototype" 来指定
-->
<bean name="cat" scope="prototype" class="com.spring.bean.Cat"/>

(3)测试代码

@Test
public void getBeanByPrototype() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");for (int i = 0; i < 3; i++) {Cat cat = ioc.getBean("cat", Cat.class);System.out.println(cat);}
}

细节说明:

  • 在默认情况下 scope属性是 singleton,在启动容器时, 默认就会创建 , 并放入到 singletonObjects 集合
  • 在ioc容器中, 只有一个这个bean对象
  • 当执行getBean时, 返回的的是同一个对象
  • 如果我们希望每次getBean返回一个新的Bean对象,则可以scope="prototype"
  • 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才 创 建 , 可 以 指 定 懒 加 载
    lazy-init="true" (注意默认是 false)
  • 通常情况下, lazy-init 就使用默认值 false , 在开发看来, 用空间换时间是值得的, 除非
    有特殊的要求.
  • 如果 scope="prototype"  这时你的 lazy-init 属性的值不管是 ture, 还是 false 都是在
    getBean 时候,才创建对象.

2.15 bean 的生命周期

:bean 对象创建是由 JVM 完成的,依次执行如下方法:

(1)执行构造器
(2)执行 set 相关方法
(3)调用 bean 的初始化的方法(需要配置)
(4)使用 bean
(5)当容器关闭时候,调用 bean 的销毁方法(需要配置)
应用实例

(1)创建 House.java

public class House {private String name;public House() {System.out.println("House() 构造器");}public String getName() {return name;}public void setName(String name) {System.out.println("House setName()...");this.name = name;}//初始化的方式public void init() {System.out.println("House init()..");}//销毁的方法public void destory() {System.out.println("House destory()..");}
}

(2)配置beans.xml,配置 bean 的初始化方法和销毁方法

<!-- 配置 bean 的初始化方法和销毁方法 -->
<bean id="house" class="com.spring.bean.House"init-method="init" destroy-method="destory"><property name="name" value="北京豪宅"/>
</bean>

(3)测试代码

@Test
public void beanLife() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");House house = ioc.getBean("house", House.class);System.out.println(house);//关闭容器((ConfigurableApplicationContext) ioc).close();
}

细节说明:

  • 初始化 init 方法和 destory 方法, 是程序员来指定
  • 销毁方法就是当关闭容器时,才会被调用

2.16 配置 bean 的后置处理器

说明:

(1)在 spring ioc 容器,可以配置 bean 的后置处理器

(2)该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用

(3)程序员可以在后置处理器中编写自己的代码

应用案例:

(1)创 建 后 置处 理 器 MyBeanPostProcessor.java

public class MyBeanPostProcessor implements BeanPostProcessor {/*** 在 bean 初始化之前完成某些任务* @param bean : 就是 ioc 容器返回的 bean 对象, 如果这里被替换会修改,则返回的 bean 对象也会被修改* @param beanName: 就是 ioc 容器配置的 bean 的名称* @return Object: 就是返回的 bean 对象*/public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
// TODO Auto-generated method stubSystem.out.println("postProcessBeforeInitialization 被 调 用 " + beanName + " bean= " + bean.getClass());return bean;}/*** 在 bean 初始化之后完成某些任务* @param bean : 就是 ioc 容器返回的 bean 对象, 如果这里被替换会修改,则返回的 bean 对象也会被修改* @param beanName: 就是 ioc 容器配置的 bean 的名称* @return Object: 就是返回的 bean 对象*/public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());return bean;}
}

0(2)配置beans03.xml

当我们在xml 容器配置文件 配置了 MyBeanPostProcessor,这时后置处理器对象,就会作用在该容器创建的所有Bean对象上

<!-- 配置 bean 的初始化方法和销毁方法 -->
<bean id="house" class="com.spring.bean.House"init-method="init" destroy-method="destory"><property name="name" value="北京豪宅"/>
</bean>
<!-- bean 后置处理器的配置 -->
<bean id="myBeanPostProcessor" class="com.spring.bean.MyBeanPostProcessor" />

(3)测试代码

@Test
public void testBeanPostProcessor() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");House house = ioc.getBean("house", House.class);System.out.println(house);//关闭容器((ConfigurableApplicationContext) ioc).close();
}

疑惑点说明:

  • 怎么执行到这个方法:使用 AOP(反射+动态代理+IO+容器+注解)
  • 有什么用:可以对 IOC 容器中所有的对象进行统一处理 ,比如 日志处理/权限的校/安全的验证/事务管理.
  • 针对容器的所有对象吗:是的=>切面编程特点

2.17 通过属性文件给 bean 注入值

在 spring ioc 容器,通过属性文件给 bean 注入值

应用案例:

(1)resources/ 下创建my.properties

可以在Unicode编码转换 - 站长工具 (chinaz.com)网站将中文转成unicode编码

monsterId=1000
name=\u4e4c\u9f9f\u7cbe
skill=\u7f29\u8116\u5b50

(2)修改 src\beans.xml , 继续完成配置

location="classpath:my.properties" 表示在类路径下的my.properties读取

这时我们的属性值通过${属性名}获取,这里说的 属性名 就是 my.properties文件中的 k=v 的k

<!-- 1. 通过属性文件给 bean 注入值, 2. 需要导入: xmlns:context 名字空间,并指定属性文件路径
-->
<context:property-placeholder location="classpath:my.properties"/>
<bean id="monster100" class="com.spring.bean.Monster"><property name="monsterId" value="${monsterId}"/><property name="name" value="${name}"/><property name="skill" value="${skill}"/>
</bean>

(3)测试代码

@Test
public void setProByProFile() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster100 = ioc.getBean("monster100", Monster.class);System.out.println(monster100);
}

2.18 基于 XML bean 的自动装配

在 spring ioc 容器,可以实现自动装配 bean

应用案例:

(1)创建  OrderDao.java
public class OrderDao {public void saveOrder() {System.out.println("保存 一个订单...");}
}

创建 OrderService.java

public class OrderService {private OrderDao orderDao;public OrderDao getOrderDao() {return orderDao;}public void setOrderDao(OrderDao orderDao) {this.orderDao = orderDao;}
}

创建 OrderAction.java

public class OrderAction {private OrderService orderService;public OrderService getOrderService() {return orderService;}public void setOrderService(OrderService orderService) {this.orderService = orderService;}
}

(2)配置 beans.xml

根据类型进行自动组装

<!--autowire="byType" 表示根据类型进行自动组装.
--><bean id="orderAction" autowire="byType" class="com.spring.action.OrderAction" /><bean id="orderService" autowire="byType" class="com.spring.service.OrderService"/><bean id="orderDao" class="com.spring.dao.OrderDao"/>

根据名称进行自动组装

<!--
1. 说明: autowire = "byName" 会自动去找 id 为 setXxxx 后面 Xxxx 的 bean 自动组装.
,如果找到就装配,如果找不到就报错, 比如这里的
2. <bean id="orderAction" autowire="byName" class="com.spring.bean.OrderAction" />
就 会 去 找 OrderAction 类 中 定 义 的 setOrderService 的 id 为 orderService 的
OrderService bean 组装,找到就阻装,找不到就组装失败
-->
<bean id="orderAction" autowire="byName" class="com.spring.action.OrderAction"/>
<bean id="orderService" autowire="byName" class="com.spring.service.OrderService"/>
<bean id="orderDao" class="com.spring.dao.OrderDao"/>

说明:

(1)autowire="byType" 表示 在创建 orderService时通过类型的方式 给对象属性 自动完成赋值/引用

(2)比如OrderService 对象有 private OrderDao orderDao

(3)就会在容器中去找有没有 OrderDao类型对象

(4)如果有,就会自动的装配, 如果是按照 byType 方式来装配, 这个容器中,不能有两个OrderDao类型对象

(5)如果对象没有属性,  autowire就没有必要写

(6)如果设置的是 autowire="byName" 表示通过名字完成自动装配

(7)比如下面的 autowire="byName" class="com.hspedu.spring.service.OrderService"

  • 先看 OrderService 属性 private OrderDao orderDao
  • 再根据这个属性的setXxx()方法的 xxx 来找对象id(而不是根据属性名)
  • public void setOrderDao() 就会找id=orderDao对象来进行自动装配
  • 如果没有就装配失败

2.19 spring el 表达式

(1)Spring Expression Language,Spring 表达式语言,简称 SpEL。支持运行时查询并可以操 作对象。

(2)和 EL 表达式一样,SpEL 根据 JavaBean 风格的 getXxx()setXxx()方法定义的属性访问对象

(3)SpEL 使用#{…}作为定界符,所有在大框号中的字符都将被认为是 SpEL 表达式。

应用案例:

(1)创建 SpELBean.java

public class SpELBean {private String name;private Monster monster;private String monsterName;private String crySound;private String bookName;private Double result;public SpELBean() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public Monster getMonster() {return monster;}public void setMonster(Monster monster) {this.monster = monster;}public String getMonsterName() {return monsterName;}public void setMonsterName(String monsterName) {this.monsterName = monsterName;}public String getCrySound() {return crySound;}public void setCrySound(String crySound) {this.crySound = crySound;}public String getBookName() {return bookName;}public void setBookName(String bookName) {this.bookName = bookName;}public Double getResult() {return result;}public void setResult(Double result) {this.result = result;}public String cry(String sound) {return "发出 " + sound + "叫声...";}public static String read(String bookName) {return "正在看 " + bookName;}
}

(2)配置 beans.xml

<!-- spring el 表达式 -->
<bean id="spELBean" class="com.spring.bean.SpELBean"><!-- sp el 给字面量 --><property name="name" value="#{'小苏'}"/><!-- sp el 引用其它 bean --><property name="monster" value="#{monster01}"/><!-- sp el 引用其它 bean 的属性值 --><property name="monsterName" value="#{monster01.name}"/><!-- sp el 调用普通方法(返回值) 赋值 --><property name="crySound" value="#{spELBean.cry('喵喵的..')}"/><!-- sp el 调用静态方法(返回值) 赋值 --><property name="bookName" value="#{T(com.spring.bean.SpELBean).read(' 天龙八部')}"/><!-- sp el 通过运算赋值 --><property name="result" value="#{89*1.2}"/>
</bean><!--配置一个monster对象-->
<bean id="monster01" class="com.spring.bean.Monster"><property name="monsterId" value="100"/><property name="name" value="蜈蚣精~"/><property name="skill" value="蜇人~"/>
</bean>

(3)测试代码

@Test
public void setProBySpel() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans04.xml");SpELBean spELBean = ioc.getBean("spELBean", SpELBean.class);System.out.println(spELBean.getName());System.out.println(spELBean.getMonster());System.out.println(spELBean.getMonsterName());System.out.println(spELBean.getCrySound());System.out.println(spELBean.getBookName());System.out.println(spELBean.getResult());
}

基于注解配置 bean

3.1 基本介绍

基于注解的方式配置 bean, 主要用于项目开发中的组件,比如 ControllerService、和 Dao.

组件注解的形式有:

(1)@Component 表示当前注解标识的是一个组件

(2)@Controller 表示当前注解标识的是一个控制器,通常用于 Servlet

(3)@Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service

(4)@Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao

3.2 快速入门案例

使用注解的方式来配置 Controller / Service / Respository / Component

(1)创建 UserAction.java UserService.java, UserDao.java MyComponent.java

/*** @Controller 标识该类是一个控制器Controller, 通常这个类是一个Servlet*/
@Controller
public class UserAction {}
/*** @Service 标识该类是一个Service类/对象*/
@Service
public class UserService {
}
/*** 使用 @Repository 标识该类是一个Repository是一个持久化层的类/对象*/
@Repository
public class UserDao {
}
/*** @Component 标识该类是一个组件, 是一个通用的注解*/
@Component
public class MyComponent {
}

(2)在xml文件中配置要扫描的包

<!--配置容器要扫描的包
1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
2. base-package 指定要扫描的包
3. 含义是当spring容器创建/初始化时,就会扫描com.spring.component包下的所有的 有注解 @Controller / @Service / @Respository / @Component类将其实例化,生成对象,放入到ioc容器
-->
<context:component-scan base-package="com.spring.component"/>

(3)测试代码

@Test
public void getBeanByAnnotation() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");UserAction userAction = ioc.getBean(UserAction.class);System.out.println(userAction);UserDao userDao = ioc.getBean(UserDao.class);System.out.println(userDao);MyComponent myComponent = ioc.getBean(MyComponent.class);System.out.println(myComponent);UserService userService = ioc.getBean(UserService.class);System.out.println(userService);
}

运行结果:

 (5)细节说明:

  • 必须在 Spring 配置文件中指定"自动扫描的包"IOC 容器才能够检测到当前项目中哪些类被标识了注解, 注意到导入 context 名称空间。可以使用通配符 * 来指定 ,比如 com.spring.* 表示。
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.spring.component" />
  • Spring IOC 容器不能检测一个使用了 @Controller 注解的类到底是不是一个真正的控
    制器。注解的名称是用于程序员自己识别当前标识的是什么组件。其它的 @Service
    @Repository 也是一样的道理( 也就是说 spring IOC 容器只要检查到注解就会生成对象,
    但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的)
  • <context:component-scan base-package="com.hspedu.spring.component" resource-pattern="User*.class" />,resource-pattern="User*.class": 表示只扫描com.spring.component 和它的子包下的User打头的类
  • 排除指定类

如果我们希望排除某个包/子包下的某种类型的注解,可以通过exclude-filter来指定

(1)context:exclude-filter 指定要排除哪些类
(2)type 指定排除方式 ,annotation表示按照注解来排除
(3)expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径

<context:component-scan base-package="com.spring.component"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

以上配置表示不扫描带Service和Repository注解的类

  • 扫描指定类

如果我们希望按照自己的规则,来扫描包/子包下的某些注解, 可以通过 include-filter

(1)use-default-filters="false" 表示不使用默认的过滤机制/扫描机制(这个一定要有)
(2)context:include-filter 表示要去扫描哪些类
(3)type="annotation" 按照注解方式来扫描/过滤
(4)expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径

<context:component-scan base-package="com.spring.component" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/><context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

 以上配置表示只扫描com.spring.component包下,带Service、Repository、Controller注解的类

  • 标记注解后,默认类名首字母小写作为 id 的值。也可以使用注解的 value 属性 指定 id 值,并且 value 可以省略。
@Controller(value="userAction01")//value可以省略
@Controller("userAction01")

3.3 实现简单的 Spring 基于注解配置的程序

3.3.1 实现思路

3.3.2 代码实现

说明:以下实现代码基于上方入门案例中的四个类

(1)ComponentScan.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {//表示ComponentScan注解可以传入一个value属性String value() default "";
}

(2)建WwjSpringConfig.java

/*** 这是一个配置类,作用类似于原生Spring的 beans.xml 容器配置文件*/
@ComponentScan(value = "com.spring.component")
public class WwjSpringConfig {
}

(3)建WwjSpringApplicationCo ntext.java

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** 该类作用类似于Spring原生ioc容器*/
public class WwjSpringApplicationContext {//拿到配置类.class文件private Class configClass;//ioc存放的就是通过反射创建的对象private final ConcurrentHashMap<String,Object> ioc = new ConcurrentHashMap<>();//构造器public WwjSpringApplicationContext(Class configClass){this.configClass = configClass;
//        System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先获取到ComponentScan注解ComponentScan componentScan = (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.获取到注解的value值String path = componentScan.value();
//        System.out.println("要扫描的包= " + path);//得到要扫描的包下的所有class文件(在target目录下)//1.得到类的加载器ClassLoader classLoader = WwjSpringApplicationContext.class.getClassLoader();//2.通过类的加载器获取到要扫描的包的资源路径path = path.replace(".","/");//一定要把.替换成/URL resource = classLoader.getResource(path);
//        System.out.println(resource);//3.将要加载的资源(.class) 路径下的文件进行遍历File file = new File(resource.getFile());if (file.isDirectory()){File[] files = file.listFiles();for (File f : files) {
//                System.out.println(f.getAbsolutePath());//获取到.class文件的绝对路径String fileAbsolutePath = f.getAbsolutePath();//这里只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//获取到类全类名//1.获取到类名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//                System.out.println(className);//2.拼接成全类名String classFullName = path.replace("/",".") + "." + className;
//                    System.out.println(classFullName);//3.判断该类是不是需要注入容器,看该类是不是有注解 @Component @Service @Repository @Controllertry {//这时,就得到了该类的Class对象//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 也可以反射类的Class//3. 区别是 : 上面方式会调用该类的静态方法, 下面方法不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @ComponentClass<?> aClass = classLoader.loadClass(classFullName);//判断该类是否有 @Component @Service @Repository @Controllerif (aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class) ||aClass.isAnnotationPresent(Controller.class)){String keyVal = StringUtils.uncapitalize(className);//获取到value值,替换指定idif (aClass.isAnnotationPresent(Component.class)){Component annotation = aClass.getDeclaredAnnotation(Component.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}} else if (aClass.isAnnotationPresent(Service.class)) {Service annotation = aClass.getDeclaredAnnotation(Service.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}} else if (aClass.isAnnotationPresent(Repository.class)) {Repository annotation = aClass.getDeclaredAnnotation(Repository.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}} else if (aClass.isAnnotationPresent(Controller.class)) {Controller annotation = aClass.getDeclaredAnnotation(Controller.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}}//这时候就可以创建该类的对象,并放入到ioc容器中Class<?> clazz = Class.forName(classFullName);Object o = clazz.newInstance();
//                            String defaultID = className.substring(0,1).toLowerCase() + className.substring(1);
//                            System.out.println(defaultID);//org.springframework.util.StringUtils包中的静态方法可以将字符串首字母小写ioc.put(keyVal,o);}} catch (Exception e) {e.printStackTrace();}}}}}public Object getBean(String id){return ioc.get(id);}
}

(4)创建测试主程序WwjSpringApplicationContextTest.java

public class WwjSpringApplicationContextTest {public static void main(String[] args) {WwjSpringApplicationContext ioc = new WwjSpringApplicationContext(WwjSpringConfig.class);System.out.println(ioc.getBean("yss1"));System.out.println(ioc.getBean("yss2"));System.out.println(ioc.getBean("yss3"));}

(5)运行结果

3.4 自动装配

基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource

3.4.1 @AutoWired 的规则说明

(1) IOC 容器中查找待装配的组件的 类型 ,如果有唯一的 bean 匹配,则使用该 bean
(2)如待装配的类型对应的 bean IOC 容器中有多个,则使用待装配的属性的属性名作
id 值再进行查找 , 找到就装配,找不到就抛异常

3.4.2 @Resource 的规则说明

(1)@Resource 有两个属性是比较重要的 , 分别是 name type,Spring @Resource 注解的
name 属性解析为 bean 的名字 , type 属性则解析为 bean 的类型 . 所以如果使用 name
, 则使用 byName 的自动注入策略 , 而使用 type 属性时则使用 byType 自动注入策略
比如@Resource(name = "userService") 表示装配 id=userService的对象
比如@Resource(type = UserService.class) 表示按照UserService.class类型进行装配, 这时要求容器中,只能有一个这样类型的对象
(2)如果 @Resource 没有指定 name type , 则先使用 byName(name的值为属性名) 注入策略 , 如果匹配不上 ,
再使用 byType 策略 , 如果都不成功,就会报错
说明: @Autowired + @Qualifier(value = "userService02") 组合也可以完成指定 name/id 来进行自动装配,这时,是装配的 id=userService02 , 需要两个注解都需要写上

3.4.3 应用案例

(1)实现 UserAction UserService 的两级自动组装

@Service
public class UserService {//方法..public void hi(){System.out.println("UserService hi()~");}
}
/*** @Controller 标识该类是一个控制器Controller, 通常这个类是一个Servlet*/
@Controller
public class UserAction {//@Autowired 自动装配 UserService, 这时是以 UserService,class 类型对对象进行组装//如待装配的类型对应的 bean 在 IOC 容器中有多个,则是以 id=userService 的 UserService 对象进行组装@Autowiredprivate UserService userService;public void sayOk() {System.out.println("UserAction 的sayOk()");System.out.println("userAction 装配的 userService属性=" + userService);userService.hi();}//不写这个方法,也可以完成组装public void setUserService(UserService userService) {this.userService = userService;}
}

(2)测试代码

@Test
public void setProByAnnotationAutowired() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");UserAction userAction01 = ioc.getBean(UserAction.class);userAction01.sayOk();
}

3.5 泛型依赖注入

基本说明:

(1)为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的注入机制

(2)在继承关系复杂情况下,泛型依赖注入就会有很大的优越性

应用实例:

(1)各个类关系图

(2)传统方法是将 PhoneDao /BookDao 自动装配到 BookService/PhoneSerive 中,当这种继承关系多时,就比较麻烦,可以使用 spring 提供的泛型依赖注入

(3)创建 Book.java,Phone.java等类

package com.spring.depinjection;
public class Book {
}package com.spring.depinjection;public class Phone {
}package com.spring.depinjection;public abstract class BaseDao<T> {public abstract void save();
}package com.spring.depinjection;
import org.springframework.stereotype.Repository;@Repository
public class BookDao extends BaseDao<Book> {@Overridepublic void save() {System.out.println("BookDao 的 save()");}
}package com.spring.depinjection;
import org.springframework.stereotype.Repository;@Repository
public class PhoneDao extends BaseDao<Phone> {@Overridepublic void save() {System.out.println("PhoneDao 的 save()");}
}package com.spring.depinjection;
import org.springframework.beans.factory.annotation.Autowired;public class BaseService<T> {@Autowiredprivate BaseDao<T> baseDao;public void save() {baseDao.save();}
}package com.spring.depinjection;
import org.springframework.stereotype.Service;@Service
public class BookService extends BaseService<Book> {
}package com.spring.depinjection;
import org.springframework.stereotype.Service;@Service
public class PhoneService extends BaseService<Phone> {
}

 (4)修改 beans06.xml , 增加配置

<context:component-scan base-package="com.hspedu.spring.depinjection"/>

(5)测试代码

@Test
public void setProByDepinjectionAutowired() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml");BookService bookService = ioc.getBean(BookService.class);bookService.save();PhoneService phoneService = ioc.getBean(PhoneService.class);phoneService.save();
}

 (6)测试结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/10012.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无人机+三角翼:小摩托无人机技术详解

无人机与三角翼的结合&#xff0c;为航空领域带来了一种新型且独特的飞行器——“小摩托”无人机。这种无人机结合了无人机的灵活性和三角翼的飞行稳定性&#xff0c;成为了航空运动领域中的一款热门产品。以下是对“小摩托”无人机技术的详解&#xff1a; 1. 定义与特点&#…

MFC中关于CMutex类的学习

MFC中关于CMutex类的学习 最近在项目中要实现两个线程之间的同步&#xff0c;MFC中提供了4个类&#xff0c;分别是CMutex(互斥量)、CCriticalSection(临界区)、CEvent(事件对象)、CSemaphore(信号量)。有关这4个类的说明&#xff0c;大家可以参考微软官方文档&#xff1a; CM…

七、Redis三种高级数据结构-HyperLogLog

Redis HyperLogLog是用来做基数统计的算法&#xff0c;HyperLogLog在优点是&#xff0c;在输入的元素的数量或者体积非常大时&#xff0c;计算基数占用的空间总是固定的、并且非常小。在Redis里每个HyperLogLog键只需花费12KB内存&#xff0c;就可以计算接近 264 个元素的基数。…

#04 构建您的第一个神经网络:PyTorch入门指南

文章目录 前言理论基础神经网络层的组成前向传播与反向传播 神经网络设计步骤1&#xff1a;准备数据集步骤2&#xff1a;构建模型步骤3&#xff1a;定义损失函数和优化器步骤4&#xff1a;训练模型步骤5&#xff1a;评估模型结论 前言 在过去的几天里&#xff0c;我们深入了解了…

头歌实践教学平台:CG1-v2.0-直线绘制

第1关&#xff1a;直线光栅化-DDA画线算法 一.任务描述 1.本关任务 (1)根据直线DDA算法补全line函数&#xff0c;其中直线斜率0<k<1&#xff1b; (2)当直线方程恰好经过P(x,y)和T(x,y1)的中点M时&#xff0c;统一选取直线上方的T点为显示的像素点。 2.输入 (1)直线两…

使用com.google.common.collect依赖包中的Lists.transform()方法转换集合对象之后,修改集合中的对象属性,发现不生效

目录 1.1、错误描述 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;模拟代码 &#xff08;3&#xff09;运行结果 1.2、解决方案 1.1、错误描述 最近在开发过程中&#xff0c;使用到了com.google.common.collect依赖包&#xff0c;通过这个依赖包中提供的…

Vue踩坑,less与less-loader安装,版本不一致

无脑通过npm i less -D安装less之后&#xff0c;继续无脑通过npm i less-loader -D安装less-loader出现如下错误&#xff1a; 解决方法&#xff1a; 1) npm uninstall less与 npm uninstall less-loader 2) 直接对其版本&#xff1a; npm i less3.0.4 -D npm i less-loader…

es关闭开启除了系统索引以外的所有索引

1、es 开启 “删除或关闭时索引名称支持通配符” 功能 2、kibanan平台执行 POST *,-.*/_close 关闭索引POST *,-.*/_open 打开索引3、其他命令 DELETE index_* // 按通配符删除以index_开头的索引 DELETE _all // 删除全部索引 DELETE *,-.* 删除全…

鸿蒙OpenHarmony开发板解析:【系统能力配置规则】

如何按需配置部件的系统能力 SysCap&#xff08;SystemCapability&#xff0c;系统能力&#xff09;是部件向开发者提供的接口的集合。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 部件配置系统…

Java入门——类和对象(上)

经读者反映与笔者考虑&#xff0c;近期以及往后内容更新将主要以java为主&#xff0c;望读者周知、见谅。 类与对象是什么&#xff1f; C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 JAVA是基于面向对…

DDOS攻击实战演示,一次DDOS的成本有多低?

DDoS攻击成本概览 分布式拒绝服务&#xff08;DDoS&#xff09;攻击以其低廉的启动成本和惊人的破坏力著称。攻击者通过黑市轻松获取服务&#xff0c;成本从几十元人民币的小额支出到针对大型目标的数千乃至数万元不等。为了具体理解这一成本结构&#xff0c;我们将通过一个简…

每日两题 / 226. 翻转二叉树 98. 验证二叉搜索树(LeetCode热题100)

226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 以后续遍历的方式交换当前节点的左右指针 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), ri…

机器学习-12-sklearn案例03-flask对外提供服务

整体思路 训练一个模型&#xff0c;把模型保存 写一个基于flask的web服务&#xff0c;在web运行时加载模型&#xff0c;并在对应的接口调用模型进行预测并返回 使用curl进行测试&#xff0c;测试通过 再创建一个html页面&#xff0c;接受参数输入&#xff0c;并返回。 目录结…

CSS悬浮动画

<button class"btn">悬浮动画</button>.btn {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);padding: 10px 20px;width: 200px;height: 50px;background-color: transparent;border-radius: 5px;border: 2px solid powderblu…

R2S+ZeroTier+Trilium

软路由使用ZeroTier搭建远程笔记 软路由使用ZeroTier搭建远程笔记 环境部署 安装ZeroTier安装trilium 环境 软路由硬件&#xff1a;友善 Nanopo R2S软路由系统&#xff1a;OpenWrt&#xff0c;使用第三方固件nanopi-openwrt。内网穿透&#xff1a;ZeroTier。远程笔记&…

银河麒麟操作系统 v10 离线安装 Docker v20.10.9

1查看系统版本 [rootweb-0001 ~]# cat /etc/os-release NAME"Kylin Linux Advanced Server" VERSION"V10 (Tercel)" ID"kylin" VERSION_ID"V10" PRETTY_NAME"Kylin Linux Advanced Server V10 (Tercel)" ANSI_COLOR"…

pyqt动画效果放大与缩小

pyqt动画效果放大与缩小 QPropertyAnimation介绍放大与缩小效果代码 QPropertyAnimation介绍 QPropertyAnimation 是 PyQt中的一个类&#xff0c;它用于对 Qt 对象的属性进行动画处理。通过使用 QPropertyAnimation&#xff0c;你可以平滑地改变一个对象的属性值&#xff0c;例…

服务器2080ti驱动的卸载与安装

服务器2080ti驱动的卸载与安装 前言1、下载驱动2、驱动卸载与安装2.1 卸载原来驱动2.2 安装新驱动 3、查看安装情况 前言 安装transformers库&#xff0c;运行bert模型时出错&#xff0c;显示torch版本太低&#xff0c;要2.0以上的&#xff0c;所以更新显卡驱动&#xff0c;重…

基于vgg16和efficientnet卷积神经网络的天气识别系统(pytorch框架)全网首发【图像识别-天气分类】

一个能够从给定的环境图像中自动识别并分类天气&#xff08;如晴天、多云、雨天、雪天闪电等&#xff09;的系统。 技术栈&#xff1a; 深度学习框架&#xff1a;PyTorch基础模型&#xff1a;VGG16与EfficientNet任务类型&#xff1a;计算机视觉中的图像分类 模型选择 VGG16 …

1.基于python的单细胞数据预处理-归一化

目录 归一化的引入移位对数皮尔森近似残差两个归一化方法的总结 参考&#xff1a; [1] https://github.com/Starlitnightly/single_cell_tutorial [2] https://github.com/theislab/single-cell-best-practices 归一化的引入 在质量控制中&#xff0c;已经从数据集删除了低质…