首页 > Python资料 博客日记
Spring1~~~
2024-10-16 00:00:10Python资料围观34次
目录
快速入门
javaBean规范要有set方法,底层反射赋值
<!-- id表示在该java对象在spring容器中的id-->
<bean class="spring.bean.Monster" id="monster01">
<property name="Id" value="100"/>
<property name="name" value="牛魔王"/>
<property name="skill" value="芭蕉扇"/>
</bean>
public class SpringBeanTest {
@Test
public void getMonster() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans.xml");
//第一种
//Object monster01 = ioc.getBean("monster01");
Monster monster01 = (Monster) ioc.getBean("monster01");
System.out.println(monster01);
//第二种
Monster monster011 = ioc.getBean("monster01", Monster.class);
System.out.println(monster011);
//类加载路径
//D:\Atest\spring\out\production\spring
File file = new File(this.getClass().getResource("/").getPath());
System.out.println(file);
}
分析
beanDefinitionMap
table存放bean对象信息
ConcurrentHashMap$Node
singletonObjects
真正创建的对象
beanDefinitionNames
先在beanDefintionMap中查找,再判断是否单例,是的话在单例池中找到返回,否则通过反射创建再返回
实现简单基于XML配置程序
public class HspApplicationContext {
private ConcurrentHashMap<String,Object> singletonObejcts = new ConcurrentHashMap();
public HspApplicationContext(String iocBeanXmlFile) throws Exception {
//加载类路径
String path = this.getClass().getResource("/").getPath();
SAXReader reader = new SAXReader();
//得到Document对象
Document document = reader.read(path + iocBeanXmlFile);
//得到rootDocument
Element rootElement = document.getRootElement();
Element bean = (Element) rootElement.elements("bean").get(0);
//获取第一个bean的 id 和 全路径
String id = bean.attributeValue("id");
String classFullPath = bean.attributeValue("class");
//获取第一个bean的属性
List<Element> property = bean.elements("property");
Integer monsterId = Integer.parseInt(property.get(0).attributeValue("value"));
String name = property.get(1).attributeValue("value");
String skill = property.get(2).attributeValue("value");
//使用反射创建对象,属性赋值
Class<?> cls = Class.forName(classFullPath);
Monster o = (Monster)cls.newInstance();
o.setId(monsterId);
o.setName(name);
o.setSkill(skill);
//创建好的对象放入单例池
singletonObejcts.put(id, o);
}
public Object getBean(String id) {
return singletonObejcts.get(id);
}
}
Spring原生容器底层结构
id分配规则
bean不带id,系统会默认分配id,分配id的规则是 全类名#0, 全类名#1 这样的规则来分配id
<bean class="spring.bean.Monster">
</bean>
<bean class="spring.bean.Monster">
</bean>
Monster monster01 = ioc.getBean("spring.bean.Monster#0", Monster.class);
Spring管理Bean - IOC
基于XML配置Bean
底层使用反射来创建实例,默认会调用无参构造方法
bean底层存放到Map<String(id),Object(bean)>
解析xml配置文件就会创建xml里设置的所有对象
new ClassPathXmlApplicationContext("s1.xml",""s2.xml");可以写多个xml文件,可变长
不一定是自定义的bean,可以是系统存在的类,如Date
BeanFactory是ioc容器的顶级接口,负责创建Bean对象,是一个工厂,也可以用BeanFactory代替ApplicationContext
通过id获取bean
<bean class="spring.bean.Monster" id="monster01">
</bean>
Monster monster011 = ioc.getBean("monster01", Monster.class);
Object monster012 = ioc.getBean("monster01");
通过类型获取bean
public void getBeanByType() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans.xml");
Monster bean = ioc.getBean(Monster.class);
System.out.println(bean);
}
要求ioc容器中的同一个类的bean只能有一个
应用场景:比如XxxAction/Servlet/Controller, 或 XxxService 在一个线程中只需要一个对象实例(单例)的情况
通过C命名空间配置bean
简化构造器
不使用C命名空间
1. constructor-arg标签可以指定使用构造器的参数
2. index表示构造器的第几个参数 从0开始计算的
3. 除了可以通过index 还可以通过 name / type 来指定参数方式
4. 类的构造器,不能有完全相同类型和顺序的构造器,所以可以通过type来指定
<bean id="monster03" class="spring.bean.Monster">
<constructor-arg value="200" index="0"/>
<constructor-arg value="白骨精" index="1"/>
<constructor-arg value="吸人血" index="2"/>
</bean>
<bean id="monster04" class="spring.bean.Monster">
<constructor-arg value="200" name="monsterId"/>
<constructor-arg value="白骨精" name="name"/>
<constructor-arg value="吸人血" name="skill"/>
</bean>
<bean id="monster05" class="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>
使用C命名空间
<bean id="monster01" class="spring.bean.Monster"
c:_0="200" c:_0="白骨精" c:_0="吸人血"/>
<bean id="monster01" class="spring.bean.Monster"
c:monsterId="200" c:name="白骨精" c:skill="吸人血"/>
通过P命名空间配置bean
基于set方法,进行简化
//将光标放在p , 输入alt+enter , 就会自动的添加xmlns
<bean class="spring.bean.Monster" id="monster01"
p:id="100"
p:name="红孩儿"
p:skill="吐火"
p:birth-ref="birthBean"
/>
<bean id="birthBean" class="java.util.Date"/>
通过util:list进行配置bean
普通做法,如果数据共享,太繁琐,需要复用
<!--配置Bookstore-->
<bean class="spring.bean.Bookstore" id="bookstore1">
<property name="bookList">
<list>
<value>三国演义</value>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
</list>
</property>
</bean>
<bean class="spring.bean.Bookstore" id="bookstore2">
<property name="bookList">
<list>
<value>三国演义</value>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
</list>
</property>
</bean>
使用util:list指定id,直接ref引用过来
<!--定义util:list-->
<util:list id="myBookList">
<value>三国演义</value>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
</util:list>
<!--配置Bookstore-->
<bean class="spring.bean.Bookstore" id="bookstore1">
<property name="bookList" ref="myBookList"/>
</bean>
<bean class="spring.bean.Bookstore" id="bookstore2">
<property name="bookList" ref="myBookList"/>
</bean>
通过外部属性文件配置Bean
在src目录下新建xx.properties
my.properties
monsterId=1000
name=jack
skill=hello
如果v是中文,可以转成unicode编码
<!--
文件提示修改成 all problems,引入namespace
location="classpath:my.properties" 表示指定属性文件的位置
-->
<context:property-placeholder location="classpath:my.properties"/>
<!--
通过属性文件给monster对象的属性赋值
属性名通过${属性名},属性名就是my.properties文件中k=v的k
-->
<bean class="spring.bean.Monster" id="monster100">
<property name="id" value="${monsterId}"/>
<property name="name" value="${name}"/>
<property name="skill" value="${skill}"/>
</bean>
Bean信息重用(继承)
<bean id="monster10" class="spring.bean.Monster">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/>
</bean>
<!--
1. 配置Monster对象
2.但是这个对象的属性值和 id="monster10"对象属性一样
3.parent="monster10" 指定当前这个配置的对象的属性值从 id=monster10的对象来
-->
<bean id="monster11"
class="spring.bean.Monster"
parent="monster10"/>
<bean id="monster12" class="spring.bean.Monster" abstract="true">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/>
</bean>
<bean id="monster13"
class="spring.bean.Monster"
parent="monster12"/>
1. 如果bean指定了 abstract="true", 表示该bean对象, 是用于被继承
2. 本身这个bean就不能被获取/实例化
基于XML自动装配
要有set方法
DAO层
public class OrderDao {
//方法。。。
public void saveOrder() {
System.out.println("保存 一个订单...");
}
}
Service层
public class OrderService {
//OrderDao属性
private OrderDao orderDao;
//getter
public OrderDao getOrderDao() {
return orderDao;
}
//setter
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
}
Controller层
public class OrderAction {
//属性OrderService
private OrderService orderService;
//getter
public OrderService getOrderService() {
return orderService;
}
//setter
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
xml
<!--配置OrderDao对象-->
<bean class="spring.dao.OrderDao" id="orderDao"/>
<!--配置OrderService对象
1. autowire="byType" 表示 在创建 orderService时
通过类型的方式 给对象属性 自动完成赋值/引用
2. 比如OrderService 对象有 private OrderDao orderDao
3. 就会在容器中去找有没有 OrderDao类型对象
4. 如果有,就会自动的装配, 老师提示如果是按照 byType 方式来装配, 这个容器中,不能有两个
的OrderDao类型对象
5. 如果你的对象没有属性, autowire就没有必要写
6. 其它类推..
7. 如果我们设置的是 autowire="byName" 表示通过名字完成自动装配
8. 比如下面的 autowire="byName" class="com.hspedu.spring.service.OrderService"
1) 先看 OrderService 属性 private OrderDao orderDao
2) 再根据这个属性的setXxx()方法的 xxx 来找对象id
3) public void setOrderDao() 就会找id=orderDao对象来进行自动装配
4) 如果没有就装配失败
-->
<bean autowire="byName" class="spring.service.OrderService"
id="orderService"/>
<!--配置OrderAction-->
<bean autowire="byName" class="spring.web.OrderAction" id="orderAction"/>
使用byName装配,id名要与set方法名(去掉set,首字母小写)一致
在OrderAction类中,setOrderService方法名更改, class="spring.service.OrderService"的id也要更改,根据方法名找对象id
注入bean对象
简单类型使用value(除Date),非简单类型使用ref
Set注入
service层
public class MemberServiceImpl {
private MemberDAOImpl memberDAO;
public MemberServiceImpl() {
System.out.println("MemberServiceImpl() 构造器被执行");
}
public MemberDAOImpl getMemberDAO() {return memberDAO;}
public void setMemberDAO(MemberDAOImpl memberDAO) {this.memberDAO = memberDAO;}
public void add() {
System.out.println("MemberServiceImpl add() 被调用..");
memberDAO.add();
}
}
dao层
public class MemberDAOImpl {
public MemberDAOImpl() {
System.out.println("MemberDAOImpl 构造器被执行...");
}
public void add() {
System.out.println("MemberDAOImpl add()方法被执行");
}
}
name规则:必须提供set方法,去掉set,第一个字母小写
外部bean
<bean class="spring.dao.MemberDAOImpl" id="memberDAO"/>
<bean class="spring.service.MemberServiceImpl" id="memberService">
<property name="memberDAO" ref="memberDAO"/>
</bean>
内部bean
<bean class="spring.service.MemberServiceImpl" id="memberService">
<property name="memberDAO">
<bean class="spring.dao.MemberDAOImpl"/>
</property>
</bean>
构造注入
三种写法
Set注入专题
注入集合/数组/Proiperties
list有序可重复
set无需不可以重复
Map
<!--配置Master-->
<bean class="spring.bean.Master" id="master">
<property name="name" value="太上老君"/>
<!--对List属性进行配置-->
<property name="monsterList">
<list>
<!--引入其他bean-->
<ref bean="monster01"/>
<ref bean="monster02"/>
<!--内部bean-->
<bean class="spring.bean.Monster">
<property name="name" value="老鼠精"/>
<property name="Id" value="100"/>
<property name="skill" value="吃粮食"/>
</bean>
</list>
</property>
<!--对Map属性进行配置-->
<property name="monsterMap">
<map>
<entry>
<key>
<value>monster01</value>
</key>
<ref bean="monster01"/>
</entry>
<entry>
<key>
<value>monster02</value>
</key>
<ref bean="monster02"/>
</entry>
</map>
</property>
<!--给set属性赋值-->
<property name="monsterSet">
<set>
<ref bean="monster01"/>
<ref bean="monster02"/>
</set>
</property>
<!--
给数组属性赋值
array标签中使用 value 还是 bean , ref .. 要根据你的业务决定
-->
<property name="monsterName">
<array>
//数组是基本类型
<value>小妖怪</value>
<value>大妖怪</value>
//数组是对象类型,需要先给这个对象配置bean
<ref bean="w1"/>
<ref bean="w2"/>
</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>
<bean class="spring.bean.Monster" id="monster01"
p:id="100"
p:name="红孩儿"
p:skill="吐火"
/>
<bean class="spring.bean.Monster" id="monster02"
p:id="101"
p:name="红孩儿"
p:skill="吐水"
/>
级联简单类型赋值
不会把Date当成简单类型,一般采用ref给Date赋值
级联赋值,Emp需要有get对象方法,因为需要先把dept拿到,顺序不能颠倒
<bean class="spring.bean.Dept" id="dept"/>
<bean class="spring.bean.Emp" id="emp">
<property name="name" value="jack"/>
<property name="dept" ref="dept"/>
<!--级联赋值-->
<property name="dept.name" value="Java开发部门"/>
</bean>
不使用级联赋值
Bean的单例和多例
<!--配置Cat对象
1. 在默认情况下 scope属性是 singleton
2. 在ioc容器中, 只要有一个这个bean对象
3. 当程序员执行getBean时, 返回的的是同一个对象
4. 如果我们希望每次getBean返回一个新的Bean对象,则可以scope="prototype"
5. 如果bean的配置是 scope="singleton" lazy-init="true"
这时,ioc容器就不会提前创建该对象,
而是当执行getBean方法的时候,才会创建对象(执行构造器)
-->
<bean id="cat" class="spring.bean.Cat" scope="prototype" lazy-init="false">
<property name="id" value="100"/>
<property name="name" value="小花猫"/>
</bean>
细节
Bean的获取方式
1、构造方法
在xml配置bean,自动调用无参构造器实例化bean
2、简单(静态)工厂模式
public class MyStaticFactory {
private static Map<String, Monster> monsterMap;
//使用 static代码块 进行初始化
static {
monsterMap = new HashMap<>();
monsterMap.put("monster01", new Monster(100,"牛魔王","芭蕉扇"));
monsterMap.put("monster02", new Monster(200,"狐狸精","美人计"));
}
//提供一个方法,返回Monster对象
public static Monster getMonster(String key) {
return monsterMap.get(key);
}
}
会执行类的无参构造器
只需要指定工厂类的静态方法(不需要配置工厂类对象)
需要class路径
1. 通过静态工厂获取/配置bean
2. class 是静态工厂类的全路径
3. factory-method 表示是指定静态工厂类的哪个方法返回对象
4. constructor-arg value="monster02" value是指定要返回静态工厂的哪个对象5.即使有多个bean,静态工厂获取的对象都是同一个
<bean id="monster01"
class="spring.factory.MyStaticFactory"
factory-method="getMonster">
<constructor-arg value="monster01"/>
</bean>
<bean id="monster02"
class="spring.factory.MyStaticFactory"
factory-method="getMonster">
<constructor-arg value="monster02"/>
</bean>
ApplicationContext context = new ClassPathXmlApplicationContext("spr.xml");
Monster monster01 = (Monster) context.getBean("monster01");
System.out.println(monster01.getName() + " 的技能是: " + monster01.getSkill());
Monster monster02 = (Monster) context.getBean("monster02");
System.out.println(monster02.getName() + " 的技能是: " + monster02.getSkill());
3、factory-bean(工厂方法模式)
public class MyInstanceFactory {
private Map<String, Monster> monster_map;
//通过普通代码块进行初始化
{
monster_map = new HashMap<>();
monster_map.put("monster03", new Monster(300, "牛魔王~", "芭蕉扇~"));
monster_map.put("monster04", new Monster(400, "狐狸精~", "美人计~"));
}
//写一个方法返回Monster对象
public Monster getMonster(String key) {
return monster_map.get(key);
}
}
返回的方法不是静态方法,需要告诉spring框架,调用哪个对象的哪个方法获取bean
需要配置工厂类对象
不需要calss路径
1. factory-bean 指定使用哪个实例工厂对象返回bean
2. factory-method 指定使用实例工厂对象的哪个方法返回bean
3. constructor-arg value="monster03" 指定获取到实例工厂中的哪个monster
<bean class="spring.factory.MyInstanceFactory" id="myInstanceFactory"/>
<bean class="spring.factory.MyInstanceFactory" id="myInstanceFactory2"/>
<bean id="my_monster02" factory-bean="myInstanceFactory" factory-method="getMonster">
<constructor-arg value="monster03"/>
</bean>
<bean id="my_monster03" factory-bean="myInstanceFactory2" factory-method="getMonster">
<constructor-arg value="monster03"/>
</bean>
使用同一个容器,调用my_monster02,都是用的同一个实例工厂对象
(factory-bean="myInstanceFactory"),只要它不变化,构建的就是同一个对象
可以有多个实例工厂,那么即使值一样,但不是同一个对象
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans.xml");
Monster monster02 = ioc.getBean("my_monster02", Monster.class);
Monster monster03 = ioc.getBean("my_monster02", Monster.class);
System.out.println(monster02 == monster03); //true
Monster monster3 = ioc.getBean("my_monster03", Monster.class);
//monster03 和 monster3返回的值都是 monster03
4、FactoryBean接口
实现FactoryBean接口之后,factory-bean、factory-method不需要在配置文件指定
是对第3种方式的简化
public class MyFactoryBean implements FactoryBean<Monster> {
//这个就是你配置时候,指定要获取的对象对应key
private String key;
private Map<String, Monster> monster_map;
{ //代码块,完成初始化
monster_map = new HashMap<>();
monster_map.put("monster03", new Monster(300, "牛魔王~", "芭蕉扇~"));
monster_map.put("monster04", new Monster(400, "狐狸精~", "美人计~"));
}
public void setKey(String key) {
this.key = key;
}
@Override
public Monster getObject() throws Exception {
return monster_map.get(key);
}
@Override
public Class<?> getObjectType() {
return Monster.class;
}
@Override
public boolean isSingleton() {//这里指定是否返回单例,默认是true
return false;
}
}
class 指定使用的FactoryBean
key表示就是 MyFactoryBean 属性key
value就是你要获取的对象对应key
<bean id="monster" class="spring.factory.MyFactoryBean">
<property name="key" value="monster04"/>
</bean>
Monster monster = ioc.getBean("monster", Monster.class);
注入Date
只能用于系统时间
实现FactoryBean,解决自定义Date
Bean创建顺序
默认按照配置的顺序创建Bean对象
如果将两个bean顺序交换
先都创建好,再执行Service里的set方法,完成引用
Bean的生命周期---五步
创建javaBean对象(House),自己写init方法,desrtoy方法,名字自己定义
<bean class="spring.bean.House" id="house"
init-method="init"
destroy-method="destroy">
<property name="name" value="北京豪宅"/>
</bean>
手动关闭才会销毁Bean
1、Spring容器只对单例的Bean进行完整的生命周期管理
2、如果是多例的Bean,只负责将该Bean初始化完毕,只执行到第4步,使用Bean
配置bean的后置处理器---七步
不指定init-method也会执行,多例对象每次都会执行
在xml中配置后置处理器对象,会作用在该容器创建的所有bean对象后置处理器, 需要实现 BeanPostProcessor接口
bean初始化方法
对ioc容器中所有的对象进行统一处理
如果类型是House的统一改成上海豪宅
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization().. bean="
+ bean + " beanName=" + beanName);
//初步体验案例: 如果类型是House的统一改成 上海豪宅
//对多个对象进行处理/编程==>切面编程
if(bean instanceof House) {
((House)bean).setName("上海豪宅~");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization().. bean="
+ bean + " beanName=" + beanName);
return bean;
}
}
<bean class="com.hspedu.spring.bean.House" id="house"
init-method="init"
destroy-method="destroy">
<property name="name" value="大豪宅"/>
</bean>
<bean class="com.hspedu.spring.bean.House" id="house02"
init-method="init"
destroy-method="destroy">
<property name="name" value="香港豪宅"/>
</bean>
<bean class="spring.bean.MyBeanPostProcessor"
id="myBeanPostProcessor"/>
@Test
public void testBeanPostProcessor() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans02.xml");
House house = ioc.getBean("house", House.class);
System.out.println("使用house=" + house);
House house02 = ioc.getBean("house02", House.class);
System.out.println("使用house02=" + house02);
((ConfigurableApplicationContext)ioc).close();
}
new对象让Spring管理
循环依赖
丈夫类有妻子字段,妻子类有丈夫字段
singleton+set注入
当两个bean都是多例的时候才会出现异常
singleton+构造注入
无法解决
源码分析
EL表达式
@Data
public class SpELBean {
private String name;
private Monster monster;
private String monsterName;
private String crySound; //叫声
private String bookName;
private Double result;
//cry 方法会返回字符串
public String cry(String sound) {
return "发出 " + sound + "叫声...";
}
//read 返回字符串
public static String read(String bookName) {
return "正在看 " + bookName;
}
}
xml
<!--配置一个monster对象-->
<bean id="monster01" class="spring.bean.Monster">
<property name="monsterId" value="100"/>
<property name="name" value="蜈蚣精~"/>
<property name="skill" value="蜇人~"/>
</bean>
<!-- spring el 表达式使用
老师解读
1. 通过spel给bean的属性赋值
-->
<bean id="spELBean" class="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.hspedu.spring.bean.SpELBean).read('天龙八部')}"/>
<!-- sp el 通过运算赋值 -->
<property name="result" value="#{89*1.2}"/>
</bean>
标签:
上一篇:Spring容器上下文
下一篇:Spring-bean的生命周期-中篇
相关文章
最新发布
- 【Python】selenium安装+Microsoft Edge驱动器下载配置流程
- Python 中自动打开网页并点击[自动化脚本],Selenium
- Anaconda基础使用
- 【Python】成功解决 TypeError: ‘<‘ not supported between instances of ‘str’ and ‘int’
- manim边学边做--三维的点和线
- CPython是最常用的Python解释器之一,也是Python官方实现。它是用C语言编写的,旨在提供一个高效且易于使用的Python解释器。
- Anaconda安装配置Jupyter(2024最新版)
- Python中读取Excel最快的几种方法!
- Python某城市美食商家爬虫数据可视化分析和推荐查询系统毕业设计论文开题报告
- 如何使用 Python 批量检测和转换 JSONL 文件编码为 UTF-8
点击排行
- 版本匹配指南:Numpy版本和Python版本的对应关系
- 版本匹配指南:PyTorch版本、torchvision 版本和Python版本的对应关系
- Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO
- 相关性分析——Pearson相关系数+热力图(附data和Python完整代码)
- Python与PyTorch的版本对应
- Anaconda版本和Python版本对应关系(持续更新...)
- Python pyinstaller打包exe最完整教程
- Could not build wheels for llama-cpp-python, which is required to install pyproject.toml-based proj