Spring5之IOC操做Bean管理(基于xml和注解)

2022年05月13日 阅读数:3
这篇文章主要向大家介绍Spring5之IOC操做Bean管理(基于xml和注解),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

目录php

前言java

1、Spring的核心web

2、Spring的特色面试

3、Spring5的主要内容spring

4、IOC底层sql

一、IOC底层的技术数据库

二、IOC接口express

5、IOC操做Bean管理(基于xml配置文件)编程

一、bean标签建立对象说明设计模式

二、基于xml配置文件进行属性注入的方式

三、基于xml配置文件进行属性注入 

四、FactoryBean 

五、bean的做用域

六、bean生命周期

七、xml自动装配

八、外部属性文件

6、IOC操做Bean管理(基于注解的方式) 


前言

前几天Spring框架曝出RCE 0day漏洞,可致使远程代码执行 (RCE),使用JDK9及以上版本都有可能受到影响,还好博主用的是稳定的JDK8(JDK8真的是yyds)。本篇文章主要介绍一下spring核心之一的IOC/DI以及他们是如何经过XML和注解的方式实现Bean管理。天不生我小码农,码道万古如长夜,码字不易,感谢支持,固然可能有一些理解不到位的地方,欢迎指正。

1、Spring的核心

Spring是轻量级的开源JavaEE框架,Spring主要用到的设计模式有工厂模式和代理模式,通常而言,工厂模式分为三类:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)。全部的框架主要用于解耦和快速开发,能够理解为框架=注解+反射+设计模式。Spring 有两个核心部分:IOC和AOP(这也是Spring面试常问的两个东西)。

一、IOC:控制反转,把对象建立和对象之间的调用过程交给Spring进行管理(DI和IOC是同一个概念的不一样角度描述,控制反转是一种编程思想,依赖注入是实现控制反转的典型方法或者说DI是IOC的具体实现,DI须要在建立对象的基础上实现,给对象的属性进行赋值)。控制:包括对象的建立,属性的赋值以及对象之间关系的管理。反转:把原来由开发人员建立、管理对象的权限交给第三方即代码以外的spring容器进行实现,在JavaSE中,当咱们缺乏对象时,咱们能够用关键字new一个对象,这也能够称为正转。

二、AOP:面向切面,不修改源代码的状况下加强功能(原理:动态代理技术,设计模式就是代理加装饰器)。

2、Spring的特色

一、方便解耦,简化开发

二、AOP编程支持

三、方便程序的测试(可使用Junit测试单元)

四、方便和其余优秀的框架进行整合(好比博主之前所写的SSM集成框架,就是Spring集成了Mybatis等优秀框架,固然还有过期的SSH框架:Spring + Struts +Hibernate,之前老会开玩笑说学完春天学冬天,如今也用不上冬天了)

五、方便进行事务的操做

六、下降API的开发难度

3、Spring5的主要内容

博主用的是5.2.5版本的,但官网已经更新到了5.3.18版本了 

一、IOC容器

二、AOP

三、jdbcTemplate

四、事务管理

五、Spring 5新特性:Webflux(响应式编程及函数式编程)、函数式注册对象、Nullable注解等。

4、IOC底层

一、IOC底层的技术

IOC思想是基于IOC容器完成的,IOC容器的底层就是Object Factories(对象工厂,工厂顾名思义是用来生产产品的,利用对象工厂,咱们建立对象时就能够获得解脱)。IOC底层技术包括xml解析、工厂模式、反射、注解。

二、IOC接口

Spring提供IOC容器的实现有两种方式(两个接口)

BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,不提供开发人员进行使用。加载配置文件时不会建立对象,只有在获取或者使用对象时才去加载对象(懒汉式)。

ApplicationContext:继承了BeanFactory接口,是Beanactory接口的子接口,它提供了更多更强大的功能,通常由开发人员使用,在加载配置文件的时候就会对对象进行建立。(饿汉式)

ApplicationContext接口有实现类,其中有两种是加载配置文件的具体实现类

 对比源码会发现二者在前面的代码基本一致,主要是体如今后面加载配置文件的方式上,FileSystemXmlApplicationContext加载绝对路径的配置文件,ClassPathXmlApplicationContext加载类路径的配置文件。

 protected Resource getResourceByPath(String path) {
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        return new FileSystemResource(path);
    }
public ClassPathXmlApplicationContext(String path, Class<?> clazz) throws BeansException {
        this(new String[]{path}, clazz);
    }

    public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz) throws BeansException {
        this(paths, clazz, (ApplicationContext)null);
    }

    public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        Assert.notNull(paths, "Path array must not be null");
        Assert.notNull(clazz, "Class argument must not be null");
        this.configResources = new Resource[paths.length];

        for(int i = 0; i < paths.length; ++i) {
            this.configResources[i] = new ClassPathResource(paths[i], clazz);
        }

        this.refresh();
    }

    @Nullable
    protected Resource[] getConfigResources() {
        return this.configResources;
    }

5、IOC操做Bean管理(基于xml配置文件

Bean管理指的是两个操做,一为Spring建立对象,二为Spring注入属性(有构造器注入和set注入)。Bean管理操做有两种方式,一种是基于xml配置文件,另一种是基于注解。

一、bean标签建立对象说明

在Spring配置文件中,使用bean标签,标签里能够添加对应的属性,就能够实现对象的建立了。(1)bean标签的属性

id属性:惟一标识,自定义对象名称(能够没有,spring能够提供默认名称),后面获取配置的建立对象时,须要对应上

class属性:类的全限定名称,spring经过反射机制建立对象,不能是接口

spring根据id,class建立对象,并把对象放入到spring的一个map对象 中,map.put(id,对象)

(2)在建立对象的时候,默认执行的是无参构造方法,之因此会使用无参构造方法,是由于反射底层调用的是newInstance(),该方法中没有参数。如何进行验证呢?咱们在Books类中写一个带参构造方法,学过java基础的都知道,java在开发者未指定构造方法时,会自动建立一个默认的无参构造方法,当写入一个带参构造方法时,默认的无参构造便会销毁,须要手动建立。

 当不建立无参构造方法时,运行代码,会报 "No default constructor found"的错误,侧面验证了上述观点。

二、基于xml配置文件进行属性注入的方式

属性注入的方式有两种,一为set注入(也叫设值注入),二为构造注入(用的比较少)

(1)set注入

经过set方法对属性进行赋值,能够对简单数据类型进行set注入,也能够对引用数据类型进行set注入。像下面例子,是对引用类型进行注入,UserServiceImpl实现类中注入userDao对象,name属性:类里面的属性名称,ref属性:建立userDao对象bean标签的id值。

 <bean id="userService" class="com.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="userDao" class="com.dao.UserDaoImpl"/>

普通类型的set注入, 使用property标签完成属性的注入,name表示属性的名字,value表示要注入的值。

<bean id="books" class="com.pojo.Books">
        <property name="bookId" value="12"/>
        <property name="bookName" value="西游记"/>
    </bean>

(2) 构造注入

这里主要指使用有参构造方法进行属性的注入,使用constructor-arg标签,使用该标签能够不写无参构造方法。

<bean id="student" class="com.pojo.Student">
        <constructor-arg name="id" value="10"/>
        <constructor-arg name="name" value="老王"/>
    </bean>
package com.pojo;
/*使用有参构造注入*/
public class Student {
    //两个属性,name和id
    private String name;
    private int id;
    /*public Student(){
    }*/
    //有参构造
    public Student(int id,String name){
        this.id=id;
        this.name=name;
    }
    public void getStudent(){
        System.out.println(id+","+name);
    }
}
@Test
    public void testAllAug() {
        //加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        //获取所配置的对象
        Student student = context.getBean("student", Student.class);
        System.out.println(student);
        student.getStudent();

三、基于xml配置文件进行属性注入 

(1)注入属性--外部bean

建立两个类service类和dao类,并在service里调用dao里面的方法,能够注意到在建立对象的过程当中,两个bean标签的位置是并排的,只是将要注入的对象bean标签的id引入被注入的对象中。

package com.dao;

public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("dao执行更新操做");
    }
}
package com.service;

import com.dao.UserDao;

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("service执行add方法");
        userDao.update();
    }
    //建立UserDao类型属性,生成set方法
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
<?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">
    <!--service和dao对象建立-->
    <bean id="userService" class="com.service.UserServiceImpl">
        <!--注入userDao对象
        name属性:类里面的属性名称
        ref属性:建立userDao对象bean标签的id值
        -->
        <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="userDao" class="com.dao.UserDaoImpl"/>

</beans>

(2)注入属性--内部bean

须要创建一对多的关系来讲明,假设如今有员工和部门两个类,一个部门能够有多个员工,而一个员工只属于一个部门。能够看出一个类有多个属性,包括基本类型属性和引用对象引用,对于引用类型的属性使用内部bean进行属性注入,这个过程有点像Mybatis按结果嵌套查询。

package com.bean;

//员工类

public class Employees {

    private String eName;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Department department;
    public void setEName(String eName) {
        this.eName = eName;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
    //生成department的get方法

    public Department getDepartment() {
        return department;
    }

    public void add(){
        System.out.println(eName+","+gender+","+department);
    }
}
<!--内部bean-->
    <bean id="emp" class="com.bean.Employees">
        <!--先设置两个基本属性-->
        <property name="EName" value="Jack"/>
        <property name="gender" value="男"/>
        <!--设置对象属性-->
        <property name="department">
            <bean class="com.bean.Department">
                <property name="departmentName" value="财务部"/>
            </bean>
        </property>
    </bean>

(3)注入属性--级联赋值

何为级联操做?指的是多个对象之间的映射关系,级联操做在引入外部bean的同时,给外部bean设置了属性,下面用表达式的形式,直接引用外部bean的属性。该方法必须在Employees类中生成Department属性的get方法,上面实体类中已有说明,如没有会报下列错误 ,第一行的refresh方法,读过源码的都知道,这个方法出现的频率很高,是spring的核心启动方法。

<?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="emp" class="com.bean.Employees">
        <property name="EName" value="老王"/>
        <property name="gender" value="女"/>
        <property name="department" ref="dpt"/>
        <property name="department.departmentName" value="技术部"/>
    </bean>
    <bean id="dpt" class="com.bean.Department">
        <property name="departmentName" value="星星点灯"/>
    </bean>
</beans>

(4)注入属性--集合属性

能够注入数组类型、List集合、Map集合、set类型的属性,在集合里面设置对象类型值

<?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="stu" class="com.Student">
        <!-- 一、数组类型属性注入-->
        <property name="courses">
            <array>
                <value>Java课程</value>
                <value>Android开发</value>
            </array>
        </property>
        <!-- 二、list类型属性注入-->
        <property name="list">
            <list>
                <value>王富贵</value>
                <value>老王</value>
            </list>
        </property>
        <!-- 三、map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"/>
                <entry key="PHP" value="php"/>
            </map>
        </property>
        <!-- 四、set类型属性注入-->
        <property name="sets">
            <set>
                <value>Mysql</value>
                <value>Redis</value>
            </set>
        </property>
        <!--注入list集合类型,值是对象-->
        <property name="courseList">
            <list>
                <ref bean="course1"/>
                <ref bean="course2"/>
            </list>
        </property>
    </bean>
    <!--建立多个course对象-->
    <bean id="course1" class="com.Course">
        <property name="courseName" value="我爱Java"/>
    </bean>
    <bean id="course2" class="com.Course">
        <property name="courseName" value="我爱C++"/>
    </bean>
</beans>

(5)把集合注入部分提取出来

首先要在spring配置文件中引入名称空间util,再使用util标签完成list集合注入提取,实体类里用set注入

<?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: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">
    <!--一、提取list集合类型属性注入-->
    <util:list id="bookList">
        <value>JavaSE如此简单</value>
        <value>Java修仙路由</value>
        <value>MySQL从入库到跑路</value>
    </util:list>
    <!--二、提取list集合类型属性注入使用-->
    <bean id="book" class="com.Book" scope="prototype">
        <property name="list" ref="bookList"></property>
    </bean>
</beans>
package com;

import java.util.List;

public class Book {
    private List<String> list;

    public void setList(List<String> list) {
        this.list = list;
    }

}

四、FactoryBean 

Spring有两种类型的bean,一种是普通的bean,另外一种是工厂bean(FactoryBean),工厂模式的意义就是为了避免暴露对象建立的过程。

普通bean:在配置文件中定义bean类型就是返回类型

工厂bean:在配置文件中定义的bean类型能够和返回类型不同

须要注意的是,上面还提了一个BeanFactory,二者的区别仍是要搞清楚的,BeanFactory是IOC容器的基本实现,是Spring内部的使用接口。

建立一个Mybean类,实现FactoryBean接口,重写FactoryBean中的三个方法,能够看出源码中的三个方法:

getObject():返回须要注册的对象 ,若是为单例,该实例会放到Spring容器中单实例缓存池中
getObjectType():返回对象的类型
isSingleton():判断是不是单例 ,非单例时每次建立都会返回一个新的bean

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}
package com.factorybean;

import com.Course;
import org.springframework.beans.factory.FactoryBean;

public class MyBean implements FactoryBean<Course> {

    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course=new Course();
        course.setCourseName("老滑头");
        return course;
    }

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

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

五、bean的做用域

(1)在spring里面,默认的状况下,bean是单实例,当用getBean获取对象两次时,输出的引用对象地址同样

(2) 如何设置单实例仍是多实例

在spring配置文件bean标签里面有属性(scope)用于设置单实例仍是多实例。

scope属性值:

第一个值为默认值,singleton,表示单例对象,第二个值,prototype,表示多实例对象。

singleton和prototype的区别:

第一 singleton单实例,prototype多实例

第二 设置scope值是singleton的时候,在加载spring配置文件时就会建立单实例对象(饿汉式)。设置scope值是prototype的时候,不是在加载spring配置文件的时候建立对象,而是在调用getBean方法时才会建立多实例对象。能够看出当用getBean获取对象两次时,输出的引用对象地址不同。

六、bean生命周期

生命周期,即从对象的建立到对象销毁的过程

bean的生命周期(面试常问):

(1)经过构造器建立bean实例(无参构造)

(2)为bean的属性设置值和对其余bean的引用(调用set方法)

(3)把bean实例传递给bean后置处理,执行postProcessBeforeInitialization方法

(4)调用bean的初始化方法(须要进行配置)

(5)把bean实例传递给bean后置处理器,执行postProcessAfterInitialization方法

(6)bean可使用了(对象获取到了)

(7)当容器关闭的时候,调用bean的销毁方法(须要本身配置销毁的方法)

package com.bean;

public class Orders {
    private String oName;

    //无参构造方法
    public Orders() {
        System.out.println("第一步:执行无参构造建立bean实例");
    }

    public void setoName(String oName) {
        this.oName = oName;
        System.out.println("第二步:调用set方法设置对象的属性值");
    }

    //建立执行的初始化方法
    public void initMethod() {
        System.out.println("第三步:执行初始化的方法");
    }

    //建立执行的销毁方法
    public void destroyMethod() {
        System.out.println("第五步:执行销毁的方法");
    }

}
@Test
    public void test4(){
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("bean4.xml");
        Orders orders = context.getBean("orders", Orders.class);
        System.out.println("第四步:获取建立bean实例对象");
        System.out.println(orders);
        //手动让bean实例销毁
        context.close();
    }
package com.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化以前执行的方法");
        return bean;
    }

    @Override
    public  Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化以后执行的方法");
        return bean;
    }

}
<?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="orders" class="com.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oName" value="手机"/>
    </bean>
    <!--配置后置处理器-->
    <bean id="myBeanPost" class="com.bean.MyBeanPost"/>
</beans>

七、xml自动装配

根据指定的装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入。bean便签属性autowire,配置自动装配,autowire属性经常使用的两个值:byName根据属性名称注入,注入值bean的id和类属性名称同样,若是不同,属性注入失败,得出的结果为null

byType根据属性类型注入(class的类型),若是是多个属性,采起就近原则,选第一个,后面的autowire注解也会谈到。

<?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="employees" class="com.autowire.Employees" autowire="byType">
     </bean>
    <bean id="department" class="com.autowire.Department">

    </bean>
</beans>

八、外部属性文件

外部属性文件在Mybatis中常常会用到,Spring也能够经过引入外部属性文件配置数据库链接池。把外部properties属性文件引入到配置文件中,这里须要引入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">

6、IOC操做Bean管理(基于注解的方式 

使用注解进行spring配置,注解做用在类上面,方法上面,属性上面,使用注解的目的:简化xml配置,springboot就是简化spring的配置,能够实现彻底注解开发(基于注解的方式在实际应用中使用的较多)。

一、Spring针对Bean管理建立对象提供注解

(1)@Component:至关于配置文件中的<bean id="" class=""/>,泛指各类组件,就是说当咱们的类不属于各类归类的时候(不属于@Controller、@Services等的时候),咱们就可使用@Component来标注这个类

(2)@Service:service层

(3)@Controller:web层

(4)@Repository:dao层

*上面四个注解功能是同样的,均可以建立bean实例

二、基于注解方式实现对象建立

第一步 开启组件扫描

<context:component-scan base-package="com"/>

若是扫描多个包,多个包使用","隔开 ,而且扫描包的上层目录。use-default-filters="false" 表示如今不使用默认的filter,本身配置filter。

context:include-filter,设置扫描哪些内容:

<context:component-scan base-package="com.service" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

context:exclude-filter:设置哪些内容不进行扫描

<context:component-scan base-package="com.service">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

三、基于注解方式进行属性的注入 

(1)@AutoWired:根据属性类型进行自动装配

  第一步:建立service和dao对象,在service和dao类上添加建立对象注解,在注解里面value属性值能够不写,默认是类名称,首字母小写,value值和bean的id等价。

package com.dao;
import org.springframework.stereotype.Repository;

@Repository(value ="userDaoImpl")
public class UserDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("dao add..........");
    }
}
package com.service;

import com.dao.UserDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    private UserDao userDao;
    @Value(value = "老王")
    private String name;
    public void add() {
        System.out.println("service add........"+name);
        userDao.add();
    }

}

  第二步:在service里面注入dao对象,在service类里面添加dao类型的属性,在属性上使用注解。

(2)@Qualifier:根据属性名称进行注入。这个@Qualifier注解的使用要和上面的@AutoWired一块儿使用。

    @Autowired //根据类型进行注入
    @Qualifier(value = "userDaoImpl11")//根据名称进行注入

(3)@Resource:能够根据类型注入,也能够根据名称注入。根据导包来看,import javax.annotation.Resource;是 java自带的注解,不是框架里有的。

@Resource(name = "userDaoImpl11")//根据名称进行注入

(4)@Value:注入普通类型属性

@Value(value = "老王")
    private String name;

 四、彻底注解开发

建立一个配置类,替代xml配置文件。@Configuration做为配置类注解,能够替代xml配置文件,

组件扫描也能够用注解@ComponentScan(basePackages = {"com"})实现。

package com.config;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration      //做为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com"})
public class SpringConfig {

}