Spring笔记二(IOC-注解)
一. 容器IOC
lOC是lnversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出低耦合、更优良的程序。
Spring 通过 IOC 容器来管理所有Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IOC容器管理的Java 对象称为 Spring Bean,它与使用关键字 new 创建的Java 对象没有任何区别。
lOC容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程
1.1控制反转(IOC)
- 控制反转是一种思想
- 控制反转是为了降低程序耦合度,提高程序扩展力
- 控制反转,反转的是什么?
- 将对象的创建交给第三方容器负责
- 将对象和对象之间的关系维护交给第三方容器负责
1.2DI 依赖注入
DI(Dependency Injection):依赖注入,依赖注入实现了控制反转的思想。
依赖注入:
- 指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
依赖注入常见的实现方式包括两种:
- 第一种:set注入
- 第二种:构造注入
所以结论是:IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。
Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
1.3IoC容器在Spring的实现
Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:
①BeanFactory
这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
②ApplicationContext
BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
③ApplicationContext的主要实现类
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。 |
WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
二. XML管理bean
2.1 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.wu.learn.xml.User"></bean>
<bean id="user2" class="com.wu.learn.xml.User"></bean>
</beans>
2.2 bean的获取
2.2.1.通过id获取
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//通过id获取对象
User user = (User) context.getBean("user");
user.run();
User user2 = (User) context.getBean("user2");
user2.run();
2.2.2. 通过类型获取
//通过类型获取对象
User userClazz = context.getBean(User.class);
userClazz.run();
通过类型获取实例时,要求容器中的对象只能存在一个,如果存在多个会报错,此时可以使用id和类型指定获取(感觉没有必要)
No qualifying bean of type 'com.wu.learn.xml.User' available: expected single matching bean but found 2: user,user2
2.2.3.通过id和类型获取
加入类型是为了保证得到的对象是自己想要的类型,如果类型不对,会提示错误
//通过类型和id获取对象
User userClazzId = context.getBean("user",User.class);
userClazzId.run();
2.2.4.通过接口获取对象
创建UserDao 接口,UserDaoImpl实现UserDao接口,在xml中添加UserDaoImpl
public interface UserDao {
void testDao();
}
public class UserDaoImpl implements UserDao{
@Override
public void testDao() {
System.out.println("testdao..................");
}
}
<?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="userDaoImpl" class="com.wu.learn.xml.UserDaoImpl"></bean>
</beans>
测试,正常获取
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserDao bean = context.getBean(UserDao.class);
bean.testDao();
新建一个PersonDaoImpl,在bean.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="userDaoImpl" class="com.wu.learn.xml.UserDaoImpl"></bean>
<bean id="personImpl" class="com.wu.learn.xml.PersonImpl"></bean>
</beans>
使用以上测试代码报错提示
No qualifying bean of type 'com.wu.learn.xml.UserDao' available: expected single matching bean but found 2: userDaoImpl,personImpl
总结
当使用spring管理bean时,如果通过类型获取对象,需要保证容器中的对象都是单例。
2.3.依赖注入
类有属性,创建对象过程中,向属性设置值
Book对象
public class Book {
private String bookName;
private String author;
public Book() {
System.out.println("无参构造。。。。。。。。。。。。。。");
}
public Book(String bookName, String author) {
System.out.println("youyouyou参构造。。。。。。。。。。。。。。");
this.bookName = bookName;
this.author = author;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
", author='" + author + '\'' +
'}';
}
}
2.3.1.setter注入
<!-- setter注入-->
<bean id="book" class="com.wu.learn.di.Book">
<property name="bookName" value="Java"></property>
<property name="author" value="王小虎"></property>
</bean>
2.3.2.构造器注入
<!-- 构造器注入-->
<bean id="bookCon" class="com.wu.learn.di.Book">
<constructor-arg name="bookName" value="C++"></constructor-arg>
<constructor-arg name="author" value="王大虎"></constructor-arg>
</bean>
2.3.3.特殊值处理
字面量赋值
使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量
<bean id="book" class="com.wu.learn.di.Book"> <property name="bookName" value="Java"></property> </bean>
null赋值
<bean id="book" class="com.wu.learn.di.Book"> <property name="bookName"> <null></null> </property> </bean>
xml实体
特殊字符需要转义
小于号在XML文档中用来定义标签的开始,不能随便使用
解决方案一:使用XML实体来代替
<bean id="book" class="com.wu.learn.di.Book"> <property name="bookName" value="<>"></property> </bean>
CDATA节
特殊字符可以写在<![CDATA[xxxx]]>中
CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据
XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析
<bean id="book" class="com.wu.learn.di.Book"> <property name="bookName" > <value><![CDATA[a>b]]></value> </property> </bean>
2.3.4. 对象类型赋值
班级类和学生类
public class ClassInfo {
//班级名称
private String className;
public void classInfo(){
System.out.println(className+"是一个十强班级");
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
public class StudentInfo {
//班级信息
private ClassInfo classInfo;
//学生姓名
private String userName;
//学生年龄
private Integer userAge;
public void study(){
System.out.println(userName+"在"+classInfo.getClassName()+"认真学习");
classInfo.classInfo();
}
public ClassInfo getClassInfo() {
return classInfo;
}
public void setClassInfo(ClassInfo classInfo) {
this.classInfo = classInfo;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getUserAge() {
return userAge;
}
public void setUserAge(Integer userAge) {
this.userAge = userAge;
}
}
外部引用示例
<!--外部引用示例-->
<bean id="classInfo" class="com.wu.learn.di_oi.ClassInfo">
<property name="className" value="高三一班"></property>
</bean>
<bean id="studentInfo" class="com.wu.learn.di_oi.StudentInfo">
<property name="userName" value="张三"></property>
<property name="userAge" value="18"></property>
<property name="classInfo" ref="classInfo"></property>
</bean>
内部引用示例
<bean id="studentInfo2" class="com.wu.learn.di_oi.StudentInfo">
<property name="userName" value="王小虎"></property>
<property name="userAge" value="17"></property>
<property name="classInfo">
<bean id="classInfo" class="com.wu.learn.di_oi.ClassInfo">
<property name="className" value="高三二班"></property>
</bean>
</property>
</bean>
级联引用示例
<bean id="studentInfo3" class="com.wu.learn.di_oi.StudentInfo">
<property name="userName" value="李二狗"></property>
<property name="userAge" value="19"></property>
<property name="classInfo" ref="classInfo"></property>
<property name="classInfo.className" value="高三三班"></property>
</bean>
2.3.5. 数组、集合赋值
关键词 array list
<bean id="dog" class="com.wu.learn.di_arr.LactationAnimal">
<property name="lacName" value="小狗"></property>
</bean>
<bean id="milk" class="com.wu.learn.di_arr.LactationAnimal">
<property name="lacName" value="小牛"></property>
</bean>
<bean id="animal" class="com.wu.learn.di_arr.Animal">
<property name="name" value="小猫"></property>
<!--数组注入-->
<property name="brothers">
<array>
<value>老虎</value>
<value>猎豹</value>
</array>
</property>
<property name="lactationAnimal">
<list>
<bean id="milkA" class="com.wu.learn.di_arr.LactationAnimal">
<property name="lacName" value="小牛"></property>
</bean>
<bean id="milkB" class="com.wu.learn.di_arr.LactationAnimal">
<property name="lacName" value="小狗"></property>
</bean>
<!-- <ref bean="dog"></ref>-->
<!-- <ref bean="milk"></ref>-->
</list>
</property>
</bean>
2.3.6.使用util进行list map赋值
引入util
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
第五步修改后的赋值
<bean id="dogUtil" class="com.wu.learn.di_arr.Dog">
<property name="listMap" ref="animalMap"></property>
<property name="list" ref="animalList"></property>
</bean>
<util:list id="animalList">
<bean id="cat" class="com.wu.learn.di_arr.Animal">
<property name="name" value="小猫"></property>
</bean>
<bean class="com.wu.learn.di_arr.Animal">
<property name="name" value="小狗"></property>
</bean>
</util:list>
<util:map id="animalMap">
<entry>
<key>
<value>buru</value>
</key>
<bean class="com.wu.learn.di_arr.Animal">
<property name="name" value="小狗"></property>
</bean>
</entry>
</util:map>
2.3.7.p命名空间
在beans中引入 xmlns:p="http://www.springframework.org/schema/p"
....
使用
<bean id="dogP" class="com.wu.learn.di_arr.Dog"
p:list-ref="animalList" p:listMap-ref="animalMap">
</bean>
2.3.8.引入外部文件
1. 编写外部文件test.properties
userM.name="张三"
userM.age=18
userM.sex="男"
2. 实体类
@Data
public class UserOut {
private String name;
private Integer age;
private String sex;
}
3. xml配置
引入context配置-》配置需要引入的外部文件-》使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置引入的外部文件-->
<context:property-placeholder location="classpath:test.properties" file-encoding="utf-8"></context:property-placeholder>
<!-- 使用${}-->
<bean id="outUser" class="com.wu.learn.di_out.UserOut">
<property name="name" value="${userM.name}"></property>
<property name="age" value="${userM.age}"></property>
<property name="sex" value="${userM.sex}"></property>
</bean>
</beans>
2.3.9.scope配置
在写bean配置的时候,有一个scope参数,如果不填,默认是单例。可以修改为多实例。
- singleton 单例模式
- prototype 多实例模式
<bean id="outUser" class="com.wu.learn.di_out.UserOut" scope="prototype">
<property name="name" value="${userM.name}"></property>
<property name="age" value="${userM.age}"></property>
<property name="sex" value="${userM.sex}"></property>
</bean>
2.3.10. bean的生命周期
- 创建对象(调用无参构造器)
- 给对象设置属性值
- bean后置处理器(初始化前)
- 对象初始化(调用指定初始化方法)
- bean后置处理器(初始化后)
- 对象创建完成
- 对象销毁
测试对象
public class UserTest {
private String userName;
public UserTest() {
System.out.println("创建对象");
}
public void initMethod(){
System.out.println("对象初始化");
}
public void destroyMethod(){
System.out.println("对象销毁");
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
System.out.println("给对象设置属性值");
this.userName = userName;
}
}
自定义bean后置处理器(用于打印日志)
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后置处理器(初始化前)");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后置处理器(初始化后)");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
xml配置
在xml指定对象的初始化方法和销毁方法。自定义bean后置处理器需要放到IOC容器中才会生效(针对所有的IOC容器中的实例对象都生效)
<bean id="userLife" class="com.wu.learn.di_life.UserTest" init-method="initMethod" destroy-method="destroyMethod">
<property name="userName" value="张三"></property>
</bean>
<bean id="myProcessor" class="com.wu.learn.di_life.MyBeanPostProcessor"></bean>
2.3.11.factoryBean
实现FactoryBean接口,可以自定义Bean的创建过程。 应用于类似代理场景
public class UserTemp {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
import org.springframework.beans.factory.FactoryBean;
public class UserFactory implements FactoryBean<UserTemp> {
@Override
public UserTemp getObject() throws Exception {
return new UserTemp();
}
@Override
public Class<?> getObjectType() {
return UserTemp.class;
}
}
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="beanFactoryTest" class="com.wu.learn.di_factory.UserFactory"></bean>
</beans>
此时获取beanFactoryTest Bean,实际得到的是UserTemp对象
2.3.12.bean自动装配
关键词: autowire
- 值为byType时,只允许单实例
- 值为byName时,可生成多实例
controller 调用service
public class UserController {
UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void testAutoWire() {
System.out.println("controller执行");
userService.testAutowire();
}
}
public interface UserService {
void testAutowire();
}
public class UserServiceImpl implements UserService{
@Override
public void testAutowire() {
System.out.println("实现类执行了");
}
}
<?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="userController" class="com.wu.learn.diaoto.UserController" autowire="byType"></bean>
<bean id="userService" class="com.wu.learn.diaoto.UserServiceImpl"></bean>
</beans>
三 基于注解管理bean
3.1. 开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 开启组件扫描,扫描指定包及该包下所有加了相应注解的对象-->
<context:component-scan base-package="com.mhld.manage">
<!-- annotation aspectj assignable custom regex -->
<!-- <context:exclude-filter type="" expression=""/>-->
<!-- <context:include-filter type="" expression=""/>-->
</context:component-scan>
</beans>
组件扫描可以使用基础包扫描,然后指定包路径
也可以使用包含过滤扫描和不包含过滤扫描,包含以下五种扫描类型
annotation :指定注解
<!--TestAno是自定义注解类--> <context:exclude-filter type="annotation" expression="com.mhld.manage.TestAno"/>
aspectj:切入点表达式
需要引入aspect依赖 aspectjweaver
<context:exclude-filter type="aspectj" expression="com.mhld.manage.animal..*"/>
assignable:指定类
<context:exclude-filter type="assignable" expression="com.mhld.manage.TestUser"/>
custom:自定义过滤器
先自定义一个过滤器,然后在bean中使用
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; public class MyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) { System.out.println("进入到了自定义过滤器中"); //自定义过滤器逻辑 return false; } }
<context:exclude-filter type="custom" expression="com.mhld.manage.filter.MyTypeFilter"/>
regex:正则表达式
<context:exclude-filter type="regex" expression="正则表达式"/>
3.2.使用注解自定义bean
- @Component :一般用于实体类
- @Service:一般用于服务层
- @Controller:一般用于控制层
- @Repository:一般用于dao层
以上四个注解效果都一致,只是区分用途
3.3. autowire和resource
1. autowire
从controller层注入service为例。此时userService接口只有一个实现类
属性注入:常用
import com.mhld.manage.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { @Autowired private UserService userService; }
set方法注入: 不常用
@Controller public class UserController { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } }
构造方法注入: 不常用
@Controller public class UserController { private UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } }
形参注入: 不常用
@Controller public class UserController { private UserService userService; public UserController(@Autowired UserService userService) { this.userService = userService; } }
只有一个构造函数时,可以省略注解: 不常用
@Controller public class UserController { private UserService userService; public UserController(UserService userService) { this.userService = userService; } }
和qualifier注解配合使用
autowire默认注入方式是通过类型(byType)注入,当需要注入的接口有多个实现类时,会提示找不到正确的注入对象
此时配合@Qualifier注解,可以指定需要注入的对象类
@Controller public class UserController { @Autowired @Qualifier(value = "userServiceImpl") private UserService userService; }
2.resource (个人常用)
@Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?
- @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
- @Autowired注解是Spring框架自己的。
- @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
- @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
- @Resource注解用在属性上、setter方法上。
- @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。
@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。】
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个
四. 全注解开发
在基于注解管理bean时,还创建了一个xml文件开启组件扫描。此配置可通过配置注解@Configuration实现。
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.test.manage")
public class MyConfiguration {
}
测试时,通过注解配置进行上下文获取
import com.mhld.manage.config.MyConfiguration;
import com.mhld.manage.controller.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ConfigTest {
@Test
public void testConfig(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
UserController bean = context.getBean(UserController.class);
System.out.println(bean);
}
}
- 本文标签: Java Spring
- 本文链接: https://www.tianyajuanke.top/article/75
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权