【Spring5】IOC
1 Spring概念
Spring是轻量级的开源JavaEE框架。可以解决企业开发的复杂性。
Spring有两个核心部分:IOC和Aop
①IOC控制反转:把创建对象过程交给Spring管理
②Aop:面向切面,不修改源代码的情况下进行功能增强
Spring5相关jar包:spring-beans-5.2.6.RELEASE.jar
spring-context-5.2.6.RELEASE.jar
spring-core-5.2.6.RELEASE.jar
spring-expression-5.2.6.RELEASE.jar
通过Spring配置文件创建对象
@Test public void test() { // 加载Spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); // 获取配置创建的对象 user u = (user) context.getBean("user"); u.add(); }
ClassPathXmlApplicationContext对应src下的文件
FileSystemXmlApplicationContext对应磁盘路径
bean1.xml
<?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.xsd"> <bean id="user" class="com.hikaru.spring5.user"></bean> </beans>
2 IOC容器
控制反转:把对象创建和对象之间的调用过程,交给Spring进行管理。
IOC底层原理
IOC过程: xml解析+工厂模式+反射
①配置xml文件,配置创建的对象
<bean id="user" class="com.hikaru.spring5.user"></bean>
②创建工厂类
xml解析后通过反射创建对象,进一步降低耦合度。
public class userFactory { public static user getUser() { String classValue = classuser; Class clazz = Class.forName(classValue); return clazz.newInstance(); } }
比如user类路径需要修改,那么只需要修改配置文件
IOC接口
Spring提供IOC容器的两种方式(两个接口):
①BeanFactory:IOC容器基本实现,是Spring内部接口,不提供开发人员使用
加载配置文件的时候不会去创建对象,只有在获取使用的时候才创建对象
②ApplicationContext:BeanFactory接口的子接口,提供了更多更强大的功能,面向开发人员使用
加载配置文件的时候就创建对象
web项目一般选择第二种方式,当服务器启动的时候就完成这些耗时的工作
IOC操作Bean管理
两种实现方式:
①基于配置文件方式
创建对象
<bean id="user" class="com.hikaru.spring5.user"></bean>
bean标签的常用属性 | |
---|---|
id | 标签的唯一标识,不是实例名 |
class | 安全路径(包类路径) |
name | 与id类似,但是name可以用特殊字符 |
这种方式创建对象时,默认执行的是无参构造器完成对象创建,如果类中只有有参数构造器没有无参构造器则会报错
DI依赖注入(属性):
①使用set方法注入
<property name="userName" value="刘"></property>
②使用有参数构造器注入
<constructor-arg name="userName" value="ryuu"></constructor-arg> <constructor-arg index="1" value="9512891"></constructor-arg>
可以通过属性名、属性在构造器的索引号注入依赖
③使用p名称空间注入
首先添加p名称空间在配置文件中
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<bean id="user" class="com.hikaru.spring5.user" p:userName="liu" p:pwd="pwd123"> </bean>
※ xml注入其它类型属性
- 字面量
(1)空值
<property name="userName"> <null></null> </property> <property name="pwd"> <null></null> </property>
(2)包含特殊字符 <![CDATA[...]>
<property name="userName"> <value><![CDATA[<><><>]]]></value> </property>
- 注入外部bean
<bean id="service" class="com.hikaru.spring5.testDemo.service"> <property name="d" ref="daoImpl"></property> </bean> <bean id="daoImpl" class="com.hikaru.spring5.testDemo.daoImpl"> </bean>
必须对外部bean进行声明
- 注入内部bean
<bean id="emp" class="com.hikaru.spring5.testDemo.emp"> <property name="name" value="luffer"></property> <property name="d"> <bean id="d" class="com.hikaru.spring5.testDemo.dept"> <property name="deptName" value="jishubu"></property> </bean> </property> </bean>
这里不能使用单元测试,单元测试只允许使用一个构造器
4.级联赋值
一种是外部bean注入属性后再注入bean
另一种内部bean注入属性
5.注入集合属性
数组:
<property name="courses"> <array> <value>Java</value> <value>数据库</value> </array> </property>
list:
<property name="list"> <list> <value>张三</value> <value>法外狂徒</value> </list> </property>
set:
<property name="set"> <set> <value>MySQL</value> <value>Redis</value> </set> </property>
map:
<property name="map"> <map> <entry key="Java" value="java"> </entry> <entry key="PHP" value="php"> </entry> </map> </property>
工厂Bean
public class myBean implements FactoryBean<Course>{ @Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("C#"); return course; } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return false; } @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); Course c= (Course) context.getBean("myBean", Course.class); System.out.println(c); } }
工厂Bean区别于普通Bean,xml配置文件定义的Bean类型与返回类型可以不同
Bean的作用域
1 单例模式与多例模式
<bean id="myBean" class="myBean" scope="prototype"> </bean>
<bean id="myBean" class="myBean" scope="singleton"> </bean>
Spring中默认情况为单例模式,每次返回的实例都是一个实例
myBean@3d921e20 myBean@36b4cef0
上为多例模式的实例地址
Bean的生命周期
(1)通过构造器创建bean实例(无参数构造,或通过p进行有参构造)
(2)为bean的 属性 设置值和其它对象的引用(set方法)
(3)调用bean的初始化方法(需要进行配置初始化的方法)
(4)获取到bean对象进行使用
(5)当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
演示Bean的生命周期
public class Order { private String oname; public Order() { System.out.println("step1"); } public String getOname() { return oname; } public void setOname(String oname) { this.oname = oname; System.out.println("step2"); } public void initMethod() { System.out.println("step3:init Bean"); } public void destroyMethod() { System.out.println("step5:destroy Eean"); } @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); Order order = context.getBean("Order", Order.class); System.out.println("step4:get bean"); ((ClassPathXmlApplicationContext) context).close(); } }
ApplicationContext作为接口没有close方法,但是其实现类ClassPathXmlApplicationContext 有,所以这里需要进行强转或者开始使用ClassPathXmlApplicationContext生声明
XML
<bean id="Order" class="Order" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="order1"></property> </bean>
step1 step1 step2 step3:init Bean step4:get bean step5:destroy Eean
bean的后置处理器
在声明周期的第三步之前和之后:
(1)通过构造器创建bean实例(无参数构造,或通过p进行有参构造)
(2)为bean的 属性 设置值和其它对象的引用(set方法)
把bean实例传递给后置处理器的方法
(3)调用bean的初始化方法(需要进行配置初始化的方法)
把bean实例传递给后置处理器的方法
(4)获取到bean对象进行使用
(5)当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
public class postBean implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("before init"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("after init"); return bean; } }
实现BeanPostProcessor接口的bean会被当做后置处理器
<bean id="Order" class="Order" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="order1"></property> </bean> <bean class="postBean"></bean>
后置bean会对xml中的所有bean生效
输出结果:
step1 step1 step2 before init step3:init Bean after init step4:get bean step5:destroy Eean
自动装配
根据指定的装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行自动注入
根据属性类型装配
<bean id="Emp" class="Emp" autowire="byType"></bean> <bean class="Dept"> <property name="dName" value="develop"></property> </bean>
这种方式的外部bean不能声明为相同的类型
根据属性名称装配
<bean id="Emp" class="Emp" autowire="byName"></bean> <bean id="dept" class="Dept"> <property name="dName" value="develop"></property> </bean>
这种方式要求外部bean名称与bean的属性名相同
引入外部属性文件
首先添加context约束声明
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:JDBC.properties"/> <bean id="Druid" class="com.alibaba.druid.pool.DruidDataSource" > <property name="driverClassName" value="${prop.driverClassName}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.username}"></property> <property name="password" value="${prop.password}"></property> </bean>
随后通过读取外部properties文件的方式建立德鲁伊数据库连接池
这里的外部文件获取必须使用${prop.}的形式
②基于注解方式
注解是代码的特殊标记,格式:@注解名称(属性名称:属性值,属性名称:属性值...),注解可以作用在类、方法、属性上。注解的目的是简化xml配置。
Spring针对Bean管理中创建对象提供的注解:
(1) @Componet
(2) @Service
(3) @Controller
(4) @Repository
上面四个注解功能是一样的,都可以用来创建bean实例
步骤:
(1)引入jar包:spring-aop-5.2.6.RELEASE.jar
(2)配置文件中添加context约束,并在配置文件中进行组件扫描(告诉Spring哪个包、类中存在注解)
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package="service"></context:component-scan>
多个包可以用,隔开 或者使用上层包名
(3)在Bean类上添加注解
@Component public class UserService { public void add() { System.out.println("service add..."); }
或者使用@Component(value="userService"),value值对应xml配置bean的id写法,value省略时默认为第一个字母小写的类名
组件过滤扫描
<context:component-scan base-package="service" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
不使用默认扫描,只扫描注解为Component的Bean
<context:component-scan base-package="service" use-default-filters="true"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
使用默认扫描但不扫描注解为Component的Bean
注解实现属性注入:只需要在配置文件设置组件扫描,其他均通过类注解实现
①Autowired:根据属性类型自动装配
第一步 在service和dao上添加创建对象注解创建对象
第二步 在service类中的dao属性上添加Autowired注解注入dao属性
注解封装了属性的set方法,不需要再次创建
@Component public class UserService { @Autowired private UserDao userDao; public void add() { userDao.add(); }
②Qualifier:根据属性名称自动装配,需要配合Autowired使用
@Component public class UserService { @Autowired @Qualifier(value = "userDaoImpl") private UserDao userDao; public void add() { userDao.add(); }
比如UserDao有多个实现类,所以需要注解指名是哪一个,这里的userDaoImpl是实现类创建注解默认生成的首字母小写的类名
③Resource:
@Resource(name = "userDaoImpl") private UserDao userDao;
等价于@Autowired
@Qualifier(value = "userDaoImpl")
④Value:
@Value("123") private String name;
同样不需要set方法
完全注解开发
配置注解另类为配置类,替代xml配置文件
设置自动扫描组件注解
@Configuration @ComponentScan(basePackages = {"service", "dao"}) public class SpringConfig { }
加载配置类:AnnotationConfigApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步