【Spring全面详解】:学习总结(1)-IOC
1.通过IOC容器创建对象,并为其赋值
<bean id="person01" class="Person">
<property name="name" value="李白"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
<property name="email" value="libai@.com"></property>
</bean>
2、通过bean的类型获取bean对象:
@Test
public void test01(){
Person bean = context.getBean(Person.class);
System.out.println(bean);
}
存在多个同类型的bean时,需要指明id:
@Test
public void test01(){
Person bean = context.getBean("person01",Person.class);
System.out.println(bean);
}
3、通过构造器为bean的属性赋值
可以通过通过p名称空间为bean赋值,此方式的优点是更加简洁。
使用前需要导入p名称空间包(配置):
xmlns:p="http://www.springframework.org/schema/p"
<!-- 通过p名称空间为bean赋值 -->
<!-- 名称空间:在xml中是名称空间是用来防止标签重复的-->
<bean id="person06" class="Person"
p:age="18" p:email="123@abc.com" p:gender="男" p:name="小黑">
</bean>
4、如何正确的为各种属性赋值
Person类:
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author Camille
* @create 2021-03-06 18:04
*/
public class Person {
private String name;
private int age;
private String gender;
private String email;
private Car car;
private List<Book> books;
private Map<String,Object> maps;
private Properties properties;
public Person() {
}
public Person(String name, int age, String gender, String email) {
this.name = name;
this.age = age;
this.gender = gender;
this.email = email;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getEmail() {return email; }
public void setEmail(String email) { this.email = email; }
public Car getCar() {return car; }
public void setCar(Car car) {this.car = car;}
public List<Book> getBooks() { return books; }
public void setBooks(List<Book> books) {this.books = books; }
public Map<String, Object> getMaps() { return maps;}
public void setMaps(Map<String, Object> maps) {this.maps = maps;}
public Properties getProperties() {return properties; }
public void setProperties(Properties properties) {this.properties = properties;}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", email='" + email + '\'' +
", car=" + car +
", books=" + books +
", maps=" + maps +
", properties=" + properties +
'}';
}
}
Car类:
/**
* @author Camille
* @create 2021-03-07 9:39
*/
public class Car {
private String carName;
private Integer price;
private String color;
public Car() {
}
public Car(String carName, Integer price, String color) {
this.carName = carName;
this.price = price;
this.color = color;
}
public String getCarName() {return carName;}
public void setCarName(String carName) {this.carName = carName;}
public Integer getPrice() {return price;}
public void setPrice(Integer price) { this.price = price; }
public String getColor() {return color;}
public void setColor(String color) {this.color = color;}
@Override
public String toString() {
return "Car{" +
"carName='" + carName + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}
Book类:
/**
* @author Camille
* @create 2021-03-07 9:42
*/
public class Book {
private String name;
private String author;
public Book(String name, String author) {
this.name = name;
this.author = author;
}
public Book() {
}
public String getName() {return name;}
public void setName(String name) {this.name = name; }
public String getAuthor() { return author; }
public void setAuthor(String author) {this.author = author; }
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
}
1.null值
在xml配置中,如果没有为bean的对象某些属性赋值时,其属性值为默认值:基本数据类型为默认值(如int类型为0),引用数据类型为null。
2.引用类型赋值:引用其他bean、引用内部bean
引用其他bean代码示例:
<!-- 外部bean-->
<bean id="car01" class="Car">
<property name="carName" value="奔驰">
</property>
<property name="price" value="300000">
</property>
<property name="color" value="黑色">
</property>
</bean>
<bean id="person01" class="Person">
<!-- 引用其他bean
ref:代表引用外面的一个值 car=ioc.getBean("car01")-->
<property name="car" ref="car01"></property>
</bean>
引用内部bean代码示例:
<bean id="person01" class="Person">
<!-- 内部对象,可以使用bean标签创建,只能被使用,不能被获取
相当于 car = new Car(); -->
<property name="car">
<bean class="Car">
<property name="carName" value="大众"></property>
</bean>
</property>
</bean>
xml配置时,若引用内部对象,可以使用bean标签创建,此bean只能被使用,不能被获取
3.集合类型赋值(List、Map、Properties)
可以通过在List、Map、Properties标签中,依次添加元素的方式进行赋值。
代码示例:
<bean id="book01" class="Book">
<property name="name" value="西游记"></property>
<property name="author" value="吴承恩"></property>
</bean>
<bean id="person02" class="Person">
<!-- 为List类型赋值 -->
<property name="books">
<list>
<!-- list标签体中添加每一个元素 -->
<bean id="boo001" class="Book" p:name="红楼梦" p:author="曹雪芹">
</bean>
<!-- 引用外部一个元素 -->
<ref bean="book01"/>
</list>
</property>
<!-- Map<String,Object> maps; -->
<property name="maps">
<map>
<!-- entry带边一个键值对 -->
<entry key="key01" value="张三"></entry>
<entry key="key02" value="18"></entry>
<entry key="key03" value-ref="book01"></entry>
<entry key="key04">
<bean class="Car">
<property name="carName" value="本田"></property>
</bean>
</entry>
</map>
</property>
<!-- private Properties properties; -->
<property name="properties">
<!-- properties = new Properties(); 所有的k=v都是String -->
<props>
<!-- k=v都是String 值直接写在标签体中-->
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
4.util名称空间创建集合类型的bean
可以通过 util名称空间创建集合类型的bean :方便其他人引用。
使用前需要导包(配置):
xmlns:util="http://www.springframework.org/schema/util"
<!-- util名称空间创建集合类型的bean :方便其他人引用-->
<bean id="person03" class="Person">
<property name="maps" ref="myMap"></property>
</bean>
<!-- 相当于 new LinkedMap() -->
<util:map id="myMap">
<!-- 添加元素-->
<entry key="key01" value="张三"></entry>
<entry key="key02" value="18"></entry>
<entry key="key03" value-ref="book01"></entry>
<entry key="key04">
<bean class="Car">
<property name="carName" value="本田"></property>
</bean>
</entry>
</util:map>
5.级联属性赋值
级联属性:属性的属性
当一个类引用另一个类时也可以通过在Bean中分别为引用的类注入值。Spring支持级联属性的配置,Spring没有对级联属性的层级数进行限制,只要配置的Bean拥有对应于级联属性的类结构,就可以配置任意层级的级联属性。
<bean id="car01" class="Car">
<property name="carName" value="奔驰"></property>
<property name="price" value="300000"></property>
<property name="color" value="黑色"></property>
</bean>
<!-- 级联属性赋值 级联属性:属性的属性 -->
<bean id="person04" class="Person">
<!-- 为car赋值时,改变car的price -->
<property name="car" ref="car01"></property>
<property name="car.price" value="200000"></property>
</bean>
6.通过继承实现bean配置的重用
使用 parent=" " 来指定所继承的bean。
7.通过abstract属性创建一个模板bean
通过设置 属性 abstract=“true” 来使得该bean不能被获取到,只能用来继承。
<bean id="person05" class="Person" abstract="true">
<property name="name" value="小明"></property>
<property name="age" value="25"></property>
<property name="gender" value="男"></property>
<property name="email" value="xiaoming.qq.com"></property>
</bean>
<!-- parent:指定当前bean继承于哪个 -->
<bean id="person06" class="Person" parent="person05">
<property name="name" value="小刚"></property>
</bean>
8.bean之间的依赖
bean对象的创建顺序是按照配置的顺序完成的,可以通过 depends-on=“bean1,bean2” 来改变其顺序
<!-- 8.bean之间的依赖(改变创建顺序)
bean对象的创建顺序是按照配置的顺序完成的
-->
<bean id="car" class="Car" depends-on="person,book"></bean>
<bean id="person" class="Person"></bean>
<bean id="book" class="Book"></bean>
9.测试bean时的作用域,分别创建单实例和多实例的bean
通过 属性scope=" " 来设置其bean的作用域。
bean的作用域:指定bean是否单实例,xxx,默认单实例,有以下几个类型:
prototype:多实例的;
1、容器启动默认不会去创建多实例的bean
2、获取的时候创建这个bean
3、每次获取都会创建一个新的对象
singleton:单实例的;默认值
1、在容器启动完成之前就已经创建好对象,保存在容器中了
2、任何获取都是先创建好的同一个对象
request:在web环境下,同一次请求创建一个Bean实例
session:在web环境下,同一次会话创建一个bean实例
<!-- 9.bean时的作用域,分别创建单实例和多实例的bean
-->
<bean id="car1" class="Car" scope="request"></bean>
10.工厂方法创建对象
<!-- FactoryBean(是Spring规定的一个接口)
只要是这个接口的实现类,Spring都认为是一个工厂
1、ioc容器启东时不会创建实例
2、FactoryBean:获取的时候才创建对象
-->
<bean id="myFactoryBeanIImple" class="MyFactoryBeanIImple">
</bean>
public class MyFactoryBeanIImple implements FactoryBean<Book> {
/**
* getObject():工厂方法
* 返回创建的对象
*/
@Override
public Book getObject() throws Exception {
Book book=new Book();
book.setAuthor("李白");
return book;
}
/**
* getObjectType():Spring会自动调用这个方法来确认创建的对象是什么类型
* @return 返回创建对象的类型
*/
@Override
public Class<?> getObjectType() {
return Book.class;
}
/**
* isSingleton():是否是单例
* flase:不是
* ture:是的
* @return
*/
@Override
public boolean isSingleton() {
return false;
}
}
5. 创建带有生命周期方法的bean
生命周期:bean的创建到销毁。
ioc容器中注册的bean:
1)单实例bean:容器启动时就创建好,容器关闭也会销毁创建的bean
2)多实例bean:在获取容器时才创建
通过 属性 init-method=“方法名” destroy-method=“方法名” 设置该bean的初始化方法和销毁方法。
代码示例:
<bean id="book01" class="Book"
init-method="myInit" destroy-method="myDestroy" ></bean>
public class Book {
private String name;
private String author;
public void myInit(){
System.out.println("图书初始化了....");
}
public void myDestroy(){
System.out.println("图书销毁了....");
}
}
可以为bean自定义一些生命周期方法:spring在创建或者销毁的时候就会调用指定的方法自定义初始化方法和销毁方法。
单实例:构造器–>初始化方法–>(容器关闭)销毁方法
多实例:获取bean(构造器–>初始化方法–>(容器关闭)销毁方法)
后置处理器:
(容器启动)构造器–>后置处理器before–>初始化方法–>后置处理器after–>bean初始化完成
无论bean是否有初始化方法,后置处理器都会工作。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* 1)编写后置处理器的实现类
* 2)将后置处理器注册在配置文件中
*
* @author Camille
* @create 2021-03-08 20:23
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 初始化之前调用
* @param bean : 要初始化的bean
* @param beanName : bean在xml配置中的id
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+"将要调用初始化方法了...");
//返回当前传入的bean
return bean;
}
/**
* 初始化方法之后调用
* @param bean
* @param beanName bean在xml配置中的id
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+"初始化方法调用完了...");
return bean;
}
}
<bean id="beanPostProcessor" class="MyBeanPostProcessor"></bean>
6.引入外部属性文件
数据库连接池作为单实例是最好的:一个项目就一个连接池,连接池里面管理很多,连接是直接从连接池中拿可以通过Spring帮助我们创建连接池对象(管理连接池)。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="yu1356369"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
</bean>
加载外部配置文件 固定写法classpath:表示引用类路径下的一个资源
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
通过 value="${jdbc.usernamea}" 可以指定引用xxx.properties文件中的某些值。(注意不要有空格)
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.usernamea}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcurl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
7.基于xml的自动装配
为person里面的自定义属性赋值
property:手动赋值
自动赋值(自动装配):
autowire="default"/"no":不自动装配;不自动为car属性赋值
安按照某种规则自动装配
autowire="byName" :按照名字
private Car car;
以属性名作为id去容器中找到这个组件,并赋值到person;若不存在,就装配null
autowire="byType"
private Car car;
1)以属性的类型作为查找依据去容器中寻找这个组件,并赋值
若存在多个这个类型的组件,会报错:NoUniqueBeanDefinitionException:
2)没有找到就装配null
相当于car = ioc.getBean(Car.class);
autowire="constructor" :
public Person(Car car)
根据构造器进行赋值:
1)先按照有参构造器的类型进行装配,成功赋值,没有就装配null
2)如果按照类型有多个,在用参数的名作为id继续匹配,成功则赋值,没有装配null
3)不会报错
对于Link、Map等属性,会将找到的所有bean都匹配装配进去
<bean id="car" class="Car">
<property name="carName" value="宝马"></property>
</bean>
<bean id="car2" class="Car">
<property name="carName" value="奔驰"></property>
</bean>
<bean id="person01" class="Person" autowire="constructor">
</bean>
8.【SpEL测试】(Spring Expression Language)Spring表达式语言
在SpEL中使用字面量
引用其他bean
引用其他bean的某个属性值
调用静态方法
调用非静态方法
使用运算符
<bean id="person04" class="Person">
<!-- 使用字面量:${}; #{} -->
<property name="age" value="#{12.5*4}"></property>
<!-- 引用其他bean -->
<property name="car" value="#{car2}"></property>
<!-- 引用其他bean的某个属性值 -->
<property name="name" value="#{car2.color}"></property>
<!-- 调用静态方法:UUID.randomUUID().toString()
#{T(全类名).静态方法名(参数1,参数2...)}
-->
<property name="email" value="#{T(java.util.UUID).randomUUID().toString()}"></property>
<!-- 调用非静态方法; 对象名.方法名 -->
<property name="name" value="#{car2.getCarName()}"></property>
</bean>
9.通过注解分别创建Dao、Service、Controller
Controller(控制器:控制网站跳转逻辑servlet)
通过给bean上添加某些注解,可以快速的将bean加入到ioc容器中某个类上添加任何一个注解都能快速的讲这个组件加入到ioc容器的管理中。
Spring有四个注解:
@Controller:控制器;推荐给控制器层(servlet包下的)组件加这个注解
@Service:业务逻辑; 推荐业务逻辑层的组件添加这个注解:Bookervice
@Repository:给数据库(持久化层,dao层)的组件添加这个注解
@Component:给不属于以上几层的组件添加这个注解;
使用注解将组件快速的加入到容器中需要几步:
1)给要添加的组件上标四个注解中的任何一个
2)告诉Spring,自动扫描加了注解的组件,依赖context名称空间
3)一定要导入AOP包,支持注解模式
注解和配置相结合 :
<context:component-scan base-package="com.camille"></context:component-scan>-->
</beans>
context:component-scan:自动组件扫描
base-package:扫描指定的基础包:把基础包及其下面的包的所有添加了注解的类,自动扫描进ioc中
使用 context:exclude-filter 指定扫描包时不包含的类
扫描时可以排除一些不要的组件
type=“annotation” :按照注解进行排除,标注了指定注解的组件不要
expression="" :注解的全类名
type=“assignable” :指定排除某个具体的类,按照类排除
expression="" :类的全类名
type=“aspectj” : aspectj表达式
type=“custom” : 自定义一个TypeFilter实现类,通过其重写的方法来决定
type=“regex” : 正则表达式
<context:component-scan base-package="com.camille">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
使用 context:include-filter 指定扫描包时要包含的类
只扫描哪些组件;默认是全部扫描
使用时一定要禁用默认规则:use-default-filters=“false”
<context:component-scan base-package="com.camille" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
10.使用@Autowired 注解实现根据类型实现自动装配(重点)
DI(依赖注入)
@Autowired原理:
@Autowired
private BookService bookService;
1)先按照类型去容器中找到对应的组件:BookServlet bean1 = (BookServlet) ioc.getBean("bookServlet");
1.找到一个:赋值
2.没找到:抛出异常
3.找到多个
1)按照变量名作为id继续匹配:BookService(bookService)、BookServiceExt(bookService)
1.匹配上则赋值
2.没有匹配会报错
此时可以使用@Qualifier("bookService"):可以指定一个名作为id,使得Spring不再使用变量名作为id,找到就装配,没找到会报错
@Autowired标注的自动注解的属性默认是一定能装配上的:找到则装配,没有找到会报错,可以使用参数 @Autowired(required = true)指定某个属性不被设置。
@Repository("bookDao")
//@Scope(value="prototype")
public class BookDao {
public void saveBook(){
System.out.println("保存图书...");
}
}
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void save() {
System.out.println("正在调用Dao保存图书...");
bookDao.saveBook();
}
}
@Controller
public class BookServlet {
//自动装配; 自动的为这个属性赋值
@Autowired
//@Qualifier("bookService"):可以指定一个名作为id,使得Spring不再使用变量名作为id
@Qualifier("bookService")
private BookService bookServiceExt1;
public void doGet() {
bookServiceExt1.save();
}
}
其中,
-
如果资源类型的bean不止一个,默认根据 @Autowired 注解标记的成员变量作为id进一步查找bean,再进行装配
-
如果按照变量名作为id还是找不到bean,可以使用@Qualifier(“bookService”):可以指定一个名作为id,明确指明目标bean,使得Spring不再使用变量名作为id–>
-
@Autowired(required = true)指定某个属性不被设置
-
方法的形参位置使用@Qualifier(" ")注解:
@Autowired、@Resource、@Inject 都是自动装配 其中: @Autowired:属于Spring的注解,功能最强大 @Resource:j2ee,属于java的标准。 拓展性更强,如果切换另一个容器框架,@Resource还能使用,而@Autowired则不行
/**
* 方法上使用 @Autowired时,
* 1.此方法会在bean创建时自动运行
* 2.这个方法中每个参数都会自动的注入值
* @param bookDao
* @param bookServiceExt
*/
@Autowired
public void MyMethod(BookDao bookDao,@Qualifier("bookService")BookService bookServiceExt){
System.out.println("MyMethod方法运行。。。"+bookDao+"["+bookServiceExt+"]");
}
11.泛型依赖注入
示例:
public abstract class baseDao<T> {
public abstract void save();
}
@Repository
public class BookDao extends baseDao<Book> {
public void save() {
System.out.println("BookDao正在保存图书。。。");
}
}
@Repository
public class userDao extends baseDao<User> {
public void save() {
System.out.println("userDao正在保存用户。。。");
}
}
public class baseService<T> {
@Autowired
private baseDao<T> baseDao;
public void save(){
baseDao.save();
}
}
@Service
public class BookService extends baseService<Book> {
}
@Service
public class userService extends baseService<User> {
}
@Service
public class userService extends baseService<User> {
}
配置信息:
<context:component-scan base-package="camille">
</context:component-scan>
测试类:
public class Test1 {
ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test01(){
BookService bookService=ioc.getBean(BookService.class);
userService userService=ioc.getBean(userService.class);
bookService.save();
userService.save();
}
}
结果: