2021-07-28-Spring5 学习笔记

IOC的底层原理

Spring 提供了IOC容器实现的两个接口:

  • BeanFactory:IOC容器的基本实现,主要是内部调用使用的。
    用此接口时,在加载配置文件的时候,不会创建对象,只有在使用的时候才会创建对象
  • ApplicationContext:是BeanFactory的子类。
    用此接口时,在加载配置文件的时候就会创建对象

在开发的时候多用ApplicationContext。

IOC的Bean管理

基于xml方式

  1. 创建一个xml配置文件,每创建一个bean都要创建一个相对应的bean标签
    其中
  • id为唯一标识,与下面的getBean()方法里的形保持一致
  • class为bean所在的全类名
  • 创建对象的时候,默认是使用无参的构造器,所以当对应的实体类中没有无参构造器时会报错。
    <bean id="user" class="com.study.spring5_1.bean.User">
    </bean>
        ApplicationContext app = new ClassPathXmlApplicationContext("bean1_user.xml");
        User user = (User)app.getBean("user");
  1. 属性值的注入:
  • 使用set方法注入
    在xml里使用property标签。使用这个标签时对应的类的属性需要有set方法
    <bean id="user" class="com.study.spring5_1.bean.User">
        <property name="name" value="tom"></property>
    </bean>
  • 使用有参的构造器注入
    需要先声明一个有参构造器。如果这里用了有参构造器,那么即使没有声明无参构造器也不会报错了
    <bean id="user" class="com.study.spring5_1.bean.User">
        <constructor-arg name="name" value="tom"></constructor-arg>
    </bean>
  • 使用p名称空间注入
    先添加p名称空间,然后就能调用p:属性名,此种方式也得基于set方法
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.study.spring5_1.bean.User" p:name="tom">
    </bean>
</beans>
  • 特殊值的注入
    空值
    <bean id="user" class="com.study.spring5_1.bean.User">
        <property name="address">
            <null></null>
        </property>
    </bean>

特殊符号
使用!CDATA方式可以实现,格式为<![CDATA[属性值]]>

    <bean id="user" class="com.study.spring5_1.bean.User">
        <property name="address">
            <value><![CDATA[<><><><>]]></value>
        </property>
    </bean>
  • 外部bean的注入
    例子中UserService类有类型为UserDao的属性,因此在注入的时候使用ref,ref关联的是bean的id
    <bean id="userService" class="com.study.spring5_1.service.UserService">
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>
    <bean id="userDaoImpl" class="com.study.spring5_1.Dao.UserDaoImpl"></bean>
  • 内部bean的注入
    当bean与bean之间有一对多的关系时,比如user的city属性的类型是City类,此时可以用内部bean注入的方式。
    <bean id="user" class="com.study.spring5_1.bean.User">
        <property name="address" value="123123"></property>
        <property name="city">
            <bean id="city" class="com.study.spring5_1.bean.City">
                <property name="name" value="dalian"></property>
            </bean>
        </property>
    </bean>
  • 级联赋值
    第一种写法(参考外部bean的注入方式)
    第二种写法
    首先得现在user的 bean里把city的属性写出来,使用city.name的方式给City类的属性进行赋值dadada,此时city的bean里写的value就无效了,最后user输出的是dadada而不是123.
    需要注意的是使用city.name时需要在user里有getCity()方法,并且
    < bean id=“city” class=“com.study.spring5_1.bean.City”/>
    和< property name=“city” ref=“city”/>一样都不能少才可以。
    <bean id="user" class="com.study.spring5_1.bean.User">
        <property name="address" value="123123"></property>
        <property name="city" ref="city"></property>
        <property name="city.name" value="dadada"></property>
    </bean>

    <bean id="city" class="com.study.spring5_1.bean.City">
        <property name="name" value="123"></property>
    </bean>
  • 集合类型的注入
    当map类型的key都相同时,输出的是最后一次的值。
    如下例,当key都是liaoning时输出的value是shanyang。
<bean id="emp" class="com.study.spring5_1.bean.Employee">
        <property name="name">
            <array>
                <value>tom</value>
                <value>lucy</value>
            </array>
        </property>
        <property name="gender">
            <list>
                <value>male</value>
                <value>female</value>
            </list>
        </property>
        <property name="city">
            <map>
                <entry key="liaoning1" value="dalian"></entry>
                <entry key="liaoning" value="shenyang"></entry>
            </map>
        </property>
        <property name="phone">
            <set>
                <value>121212</value>
                <value>323232</value>
            </set>
        </property>
        <!-- 如果是list<User>类型的时候,可以用ref注入 bean为对应user bean的id-->
       	<property name="users">
            <list>
                <ref bean="user"></ref>
            </list>
        </property>
    </bean>

如果多个bean中都有相同的list,可以将list提取出来
首先要加命名空间util
然后使用util:list标签声明一组list
在bean中有list类型的属性通过ref引用,所有的bean都可以引用这个公共的list

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <util:list id="publicList">
        <value>11111</value>
        <value>22222</value>
    </util:list>
    <bean id="emp" class="com.study.spring5_1.bean.Employee">
    	<property name="gender" ref="publicList"></property>
    </bean>
  • FactoryBean
    与普通的bean不同的是:普通bean在xml里注册的时候class的类与返回的类保持一致,但是FactoryBean却可以不一致,
    如下例:
    先创建一个类,实现FactoryBean接口,先给FactoryBean加泛型,此时getObject方法的返回值类型也得和泛型的类型保持一致。
    返回的什么类是由getObject方法决定的,里面return什么类最后从配置文件里得到的就是什么类。
<bean id="myBean" class="com.study.spring5_1.bean.MyBean"></bean>
public class MyBean implements FactoryBean<User> {
	//这个getObject()方法决定了返回的类型
    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setAddress("123123131");
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}
    @Test
    public void testFactoryBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean1_user.xml");
        //因为返回的是User类,就得用User去接收值,否则会报错。
        User user = app.getBean("myBean", User.class);
        System.out.println(user);
    }
  • bean的作用域
    scope为singleton时,bean是单实例,spring默认是单实例
    scope为prototype时,bean是多实例
    当时prototype时,不会在引用bean的时候创建实例,而是在getBean的时候才创建实例
    <bean id="user" class="com.study.spring5_1.bean.User" scope="prototype">
        <property name="address" value="123123"></property>
        <property name="city" ref="city"></property>
        <property name="city.name" value="dadada"></property>
    </bean>

举例

    @Test
    public void testUser(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean1_user.xml");
        User user = (User)app.getBean("user");
        User user1 = (User)app.getBean("user");
        System.out.println("user : " + user.toString() + "---" + user);
        System.out.println("user1 : " + user1.toString() + "---" + user1);
        user.setName("xiaoming");
        System.out.println("user : " + user.toString() + "---" + user);
        System.out.println("user1 : " + user1.toString() + "---" + user1);
    }

单实例的时候:
注意到,单实例的时候,因为地址是同一个,所以当给user的属性赋值时,user1里的属性也跟着变化。其实是因为他们两个在jvm里指向的是同一个地址,任何一方对属性操作,所有人都会跟着变化

user : User{name='null', address='123123', city=City{name='dadada'}}---User{name='null', address='123123', city=City{name='dadada'}}
user1 : User{name='null', address='123123', city=City{name='dadada'}}---User{name='null', address='123123', city=City{name='dadada'}}
user : User{name='xiaoming', address='123123', city=City{name='dadada'}}---User{name='xiaoming', address='123123', city=City{name='dadada'}}
user1 : User{name='xiaoming', address='123123', city=City{name='dadada'}}---User{name='xiaoming', address='123123', city=City{name='dadada'}}

多实例的时候
此时对user属性赋值不会影响user1的属性值

user : User{name='null', address='123123', city=City{name='dadada'}}---User{name='null', address='123123', city=City{name='dadada'}}
user1 : User{name='null', address='123123', city=City{name='dadada'}}---User{name='null', address='123123', city=City{name='dadada'}}
user : User{name='xiaoming', address='123123', city=City{name='dadada'}}---User{name='xiaoming', address='123123', city=City{name='dadada'}}
user1 : User{name='null', address='123123', city=City{name='dadada'}}---User{name='null', address='123123', city=City{name='dadada'}}
  • bean的生命周期
1. 调用无参构造器
2. 调用属性的set方法
前置处理器
3. 调用初始化方法
后置处理器
4. 创建bean实例 //执行getBean()方法的时候
5.  调用销毁方法

初始化方法和销毁方法需要在bean里配置

    <bean id="user" class="com.study.spring5_1.bean.User" scope="prototype" init-method="initMethod" destroy-method="destoryMethod">
        <property name="address" value="123123"></property>
    </bean>

    <bean id="processor" class="com.study.spring5_1.processer.myProcesser"></bean>

处理器的创建需要实现BeanPostProcessor接口,创建完之后还得在xml里创建bean进行注入才会生效。
前后置处理器在init方法前后执行

public class myProcesser implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       System.out.println("前置处理器");
       return null;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器");
        return null;
    }
}
  • 属性的自动注入
    使用autowire可以完成属性自动注入。
    因为user里由City类型的属性,先声明City的bean。
    autowire有两种方式:
    ByName:user的属性名和city的bean id要一致才能匹配上
    ByType:根据类型自动注入,因为是City类型的,找xml里这个类型的bean进行关联。但是当有多个相同类型的bean时因为不确定关联哪个,所以会报错
    <bean id="user" class="com.study.spring5_1.bean.User" scope="prototype" autowire="byName">
    </bean>

    <bean id="city" class="com.study.spring5_1.bean.City">
        <property name="name" value="123"></property>
    </bean>

因为在xml里没有给user进行city属性的赋值,但是因为有autowire,所以city属性自动关联到了id为city的bean的值

User{name='null', address='null', city=City{name='123'}}
  • 引入外部属性
    首先要引入context命名空间
    然后使用context:property-placeholder标签引入外部的配置文件
    (可能是druid才能用prop,但是自己定义的类肯定用不了prop)
<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/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"></context:property-placeholder>

    <bean id="jdbctemplate" 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>

基于注解方式

  • 准备工作:
    • 引入spring-aop-5.3.8.jar包
    • 在配置文件里引入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/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="com.study.spring5_1"/>
</beans>

组件扫描里的配置:
可以指定包里的哪些东西扫描,哪些东西不扫描
指定哪些东西扫描:< context:include-filter >
指定哪些东西不扫描:< context:exclude-filter >

    <context:component-scan base-package="com.study.spring5_1" use-default-filters="false">
    	<!-- 表示只扫描被@Component注解标注的类 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    	<!-- 表示除了被@Component注解标注的类,剩下的都扫描 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
  • 新建一个类,用注解的方式注入
    需要注意的是:value里的值相当于bean里的id,如果不指定的话,默认是类名的第一个字母小写的值
@Component(value = "cityservice")
public class CityService {
    public void test(){
        System.out.println("cityService test method");
    }
}

测试

    @Test
    public void testAnnotion(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean_user_annotion.xml");
        CityService city = app.getBean("cityservice", CityService.class);
        city.test();
    }
  • 属性的注入(使用注解时不需要添加set方法)
    @Autowired
    @Qualifier
    @Resource(name = “”)
    @Value

    @Autowired:根据类型去注入
    @Qualifier:根据name去注入,使用此注解的时候需要结合@Autowired一起使用,
    使用场景为:当一个接口有多个实现类的时候,根据类型不知道注入哪一个实现类,此时需要指定实现类的id(默认是类型第一个字母小写)
    例子:

   @Autowired
   @Qualifier(value = "userDaoImpl")
   private UserDao userDao;

   public void test(){
       userDao.add();
       System.out.println("cityService test method");
   }

@Resource(name = “”)是autowired和qualifier的合体,既可以根据属性也可以根据name注入,当不写name的时候就是根据属性注入
@Value:对普通属性的注入

    @Value("123")
    private String name;
  • 使用全注解开发,不需要配置文件,用配置类代替
    在类名上先使用@Configuration注解
    @ComponentScan是配置扫描路径
@Configuration
@ComponentScan(basePackages = {"com.study.spring5_1"})
public class CommonConfig {
}

使用配置类之后,ApplicationContext 的获取方式变成了AnnotationConfigApplicationContext(CommonConfig.class)

    @Test
    public void testConfig(){
        ApplicationContext app = new AnnotationConfigApplicationContext(CommonConfig.class);
        CityService city = app.getBean("cityservice", CityService.class);
        city.test();

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值