Loading

Spring(一)_IOC

一、主要内容

  • Spring框架概述
  • IOC容器
  • AOP容器
  • JdbcTemplate
  • 事务管理
  • Spring5新特性

二、框架概述

2.1 概述

  • Spring是轻量级的开源的JavaEE框架,由Rod Johnson发起
  • Spring可以解决企业应用开发的复杂性
  • Spring有两个核心部分:IOC和AOP
    • IOC:控制反转,把创建对象的过程交给Spring进行管理(之前是手动new一个,Spring帮我们创建对象)。
    • AOP:面向切面,不修改源代码的情况下进行功能增强
  • Spring特点
    • 方便解耦,简化开发
    • AOP编程支持
    • 方便程序测试
    • 方便和其他框架进行整合
    • 方便进行事务操作
    • 降低API开发难度
    • Spring的源码是经典的学习范例

2.2 入门案例

  1. 在IDEA中新建Maven工程,然后导入Spring所需的依赖。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.18</version>
</dependency>

核心部分

image-20220514142850699

image-20220514142631666

  1. 创建普通类,在普通类中创建一个普通的方法
public class User {
    public void add(){
        System.out.println("add...");
    }
}
  1. 创建Spring配置文件,在配置文件中配置创建的对象

之前想要实现这个类的对象的创建,直接new这个类,调add方法就行。现在我们把这个过程交给spring来做(可以通过配置文件做到,也可以通过注解做到,这次是案例入门,所以使用配置文件)。

Spring配置文件使用xml格式,new --> XML Configuration File --> Spring Config

image-20220514144116339

<?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">
    <!--配置User对象创建-->
    <bean id="user" class="com.example.User">

    </bean>
</beans>
  1. 创建测试类,测试
package com.example;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        // 1.加载Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        // 2.根据id获取配置创建的对象
        User user = context.getBean("user", User.class);

        System.out.println(user);
        user.add();
    }
}

image-20220514144719204

三、IOC容器

3.1 概念和原理

什么是IOC

  • 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
  • 使用IOC的目的:为了降低耦合度
  • 入门案例就是IOC的实现

IOC底层原理

  • XML解析、工厂模式、反射

图解IOC底层原理

目的:将耦合度降低到最低(对象之间调用不可能完全没有耦合度)

原始方式

image-20220514150452341


工厂模式

image-20220514151551062


IOC过程

  1. xml配置文件,配置创建的对象
<bean id="dao" class="com.example.UserDao">

</bean>
  1. 有service类和dao类,创建工厂类
class UserFactory{
    public static UserDao getDao(){
        // 1.xml解析
        String classValue = class属性值;
        // 2.通过反射创建对象
        Class clazz = Class.forName(classValue);
        return (UserDao) clazz.newInstance();
    }
}

进一步地降低了耦合度,当UserDao类名更改时,不再需要更改任何的类,只需要更改xml配置文件即可。

原始方式时,当我们更改了UserDao的类名时,那么所有调用该对象的类都得更改,耦合度非常高。

当有了工厂后,只需要更改工厂里的创建对象的类名就解耦了。

再配合xml的配置和反射技术,只需更新xml的配置,就实现了最佳解耦

3.2 接口

  1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。

  2. Spring提供IOC容器实现的两种方式:

    1. BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员使用。

      加载配置文件的时候不会创建对象,在获取对象(使用)时才去创建对象。

    2. ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用(开发中比较常用)。

      加载配置文件时就会把配置文件中的<bean>标签对象进行创建。

    Spring通常配合Web进行开发,创建对象这种耗时耗资源的操作最好都放在启动时处理,在服务器启动的时候就去创建这些对象。

    所以选择 ApplicationContext

    做到慢启动,快响应。

3.3 Bean管理(概念)

什么是Bean管理

Bean管理指的是两个操作:

  1. Spring创建对象
  2. Spring注入属性

管理操作有两种方式

  1. 基于xml配置文件方式实现
  2. 基于注解方式实现

3.4 Bean管理(基于xml方式)

基于xml方式创建对象

<!--配置User对象创建-->
<bean id="user" class="com.example.User">

</bean>
  1. 在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
  2. 在bean标签中有很多属性,介绍常用的属性:
    • id:唯一标识
    • class:类的全路径(包类路径)
  3. 创建对象时,默认是执行无参构造方法完成对象创建

基于xml方式注入属性

DI:依赖注入,就是注入属性,需要在创建对象的基础之上完成。

第一种方式:使用set方法进行注入

第二种方式:使用有参构造方法进行注入

使用set方法进行注入

  1. 创建类,定义属性和对应的set方法
public class Book {
    private String bookName;
    private String bookAuthor;

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public void setBookAuthor(String bookAuthor) {
        this.bookAuthor = bookAuthor;
    }

    public void print(){
        System.out.println("bookName = " + bookName);
        System.out.println("bookAuthor = " + bookAuthor);
    }
}
  1. 在Spring配置文件中配置对象创建,配置属性注入
<!--set方法注入属性-->
<bean id="book" class="com.example.Book">
    <!--使用property完成属性注入
            name:类里面的属性名称
            value:向属性注入的值
        -->
    <property name="bookName" value="活着"/>
    <property name="bookAuthor" value="余华"/>
</bean>

测试

public class TestSpring {
    @Test
    public void testBook(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        Book book = context.getBean("book", Book.class);
        System.out.println("book = " + book);
        book.print();
    }
}

image-20220514164203274

使用有参构造方法进行注入

  1. 创建类,定义属性,创建属性对应有参构造方法。
public class Orders {
    private String orderName;
    private String orderAdd;

    public Orders(String orderName, String orderAdd) {
        this.orderName = orderName;
        this.orderAdd = orderAdd;
    }

    public void print(){
        System.out.println("orderName = " + orderName);
        System.out.println("orderAdd = " + orderAdd);
    }
}
  1. 在Spring配置文件中配置对象创建,配置属性注入
<!--有参构造方法-->
<bean id="orders" class="com.example.Orders">
    <!--通过参数名称注入(用得多)-->
    <constructor-arg name="orderName" value="瓦香田鸡"/>
    <constructor-arg name="orderAdd" value="中8"/>
</bean>
===============================或==================================
<bean id="orders" class="com.example.Orders">
    <!--通过索引注入(用得少)-->
    <constructor-arg index="0" value="瓦香田鸡"/>
    <constructor-arg index="1" value="中8"/>
</bean>

测试

@Test
public void testOrders(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("orders = " + orders);
    orders.print();
}

image-20220514165125822

3.5 Bean管理(xml注入其他类型属性)

字面量

null值

<property name="bookAddr">
    <!--为该属性设置null值-->
    <null/>
</property>

属性值包含特殊符号

<!--会因为特殊符号而报错-->
<property name="bookAddr" value="<<中8>>"/>
<!--属性值包含特殊符号
	1 用<>进行转义 &lt;&gt;
	2 把带特殊符号的内容写到CDATA中
-->
<property name="bookAddr">
    <value><![CDATA[<<南京>>]]></value>
</property>

注入属性-外部bean

  1. 创建两个类:service类和dao类
public interface UserDao {
    void update();
}
public class UserDaoImpl implements UserDao{
    public void update() {
        System.out.println("dao update ...........");
    }
}
  1. 在service调用dao里面的方法
public class UserService {
    // 创建UserDao类型属性,生成set方法。
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add(){
        System.out.println("service add ................");
        userDao.update();
    }
}
  1. 在Spring配置文件中进行配置
<!--service和dao对象的创建-->
<bean id="userService" class="com.example.service.UserService">
    <!--注入userDao对象,注入其他对象
        name属性:类里面属性名称
        ref属性:要注入的对象的bean标签的id值
    -->
    <property name="userDao" ref="userDaoImpl"/>
</bean>

<!--外部bean-->
<bean id="userDaoImpl" class="com.example.dao.UserDaoImpl"/>

测试

@Test
public void testUser(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println("userService = " + userService);
    userService.add();
}

image-20220514173240441

注入属性-内部bean

一对多

部门和员工

  1. 一个部门有多个员工,一个员工属于一个部门。部门是一,员工是多。
public class Dept {
    private String deptName;

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptName='" + deptName + '\'' +
                '}';
    }
}
  1. 在实体类中表示一对多关系,员工表示所属部门,使用对象类型属性进行表示。
public class Emp {
    private String empName;
    private String gender;
    // 员工属于哪一个部门,使用对象形式表示
    private Dept dept;

    public void setEmpName(String empName) {
        this.empName = empName;
    }

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

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void print(){
        System.out.println("empName = " + empName);
        System.out.println("gender = " + gender);
        System.out.println("dept = " + dept);
    }

    @Override
    public String toString() {
        return "Emp{" +
                "empName='" + empName + '\'' +
                ", gender='" + gender + '\'' +
                ", dept=" + dept +
                '}';
    }
}
  1. 在Spring配置文件中进行配置
<bean id="emp" class="com.example.bean.Emp">
    <property name="empName" value="lucy"/>
    <property name="gender" value="女"/>
    <!--内部bean-->
    <property name="dept">
        <bean class="com.example.bean.Dept">
            <property name="deptName" value="保安部"/>
        </bean>
    </property>
</bean>

测试

@Test
public void testEmpDept(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    Emp emp = context.getBean("emp", Emp.class);
    System.out.println("emp = " + emp);
    emp.print();
}

image-20220514180120683

注入属性-级联赋值

注入外部bean

  1. 修改Spring配置文件
<bean id="emp" class="com.example.bean.Emp">
    <property name="empName" value="lucy"/>
    <property name="gender" value="女"/>
    <!--设置外部bean-->
    <property name="dept" ref="dept"/>
</bean>
<bean id="dept" class="com.example.bean.Dept">
    <property name="deptName" value="安保部"/>
</bean>

测试

@Test
public void testEmpDept(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Emp emp = context.getBean("emp", Emp.class);
    System.out.println("emp = " + emp);
    emp.print();
}

image-20220514180648159

级联赋值

<bean id="emp" class="com.example.bean.Emp">
    <property name="empName" value="lucy"/>
    <property name="gender" value="女"/>
    <property name="dept" ref="dept"/>
    <!--采用级联赋值需要给当前bean中的该变量赋get方法-->
    <property name="dept.deptName" value="技术部"/>
</bean>
<bean id="dept" class="com.example.bean.Dept">
    <property name="deptName" value="财务部"/>
</bean>

image-20220515000147057

3.6 Bean管理(xml注入集合属性)

  1. 创建类,定义数组、List、Map、Set类型属性。(用Lombok的@Data注解自动配置getter和setter)
@Data
public class Student {
    // 1.数组类型属性
    private String[] courses;
    // 2.list集合类型属性
    private List<String> list;
    // 3.map集合类型属性
    private Map<String, String> maps;
    // 4.set集合类型属性
    private Set<String> sets;

    public void print(){
        System.out.println(Arrays.toString(courses));
        System.out.println(list);
        System.out.println(maps);
        System.out.println(sets);
    }
}

注入数组类型属性

  1. 在Spring配置文件中进行配置
<!--数组属性注入-->
<property name="courses">
    <array>
        <value>Java课程</value>
        <value>MySQL课程</value>
    </array>
</property>

注入List集合类型属性

<!--List集合属性注入-->
<property name="list">
    <list>
        <value>张三</value>
        <value>法外狂徒</value>
    </list>
</property>

注入Map集合

<!--Map集合属性注入-->
<property name="maps">
    <map>
        <entry key="JAVA" value="java"/>
        <entry key="MYSQL" value="mysql"/>
    </map>
</property>

注入Set集合

<!--Set集合属性注入-->
<property name="sets">
    <set>
        <value>MySQL</value>
        <value>Redis</value>
    </set>
</property>

总的集合类型bean配置

<!--集合类型属性注入-->
<bean id="student" class="com.example.spring.collectiontype.Student">
    <!--数组属性注入-->
    <property name="courses">
        <array>
            <value>Java课程</value>
            <value>MySQL课程</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="MYSQL" value="mysql"/>
        </map>
    </property>
    <!--Set集合属性注入-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
</bean>

测试

@Test
public void test01(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Student student = context.getBean("student", Student.class);
    student.print();
}

image-20220515115043990

在集合里面设置对象类型值

  1. 创建一个实体类 Course
@Data
public class Course {
    private String cname;
}
  1. 在配置文件中创建三个 Course 对象
<bean id="course1" class="com.example.spring.collectiontype.Course">
    <property name="cname" value="Spring"/>
</bean>

<bean id="course2" class="com.example.spring.collectiontype.Course">
    <property name="cname" value="Mybatis"/>
</bean>

<bean id="course3" class="com.example.spring.collectiontype.Course">
    <property name="cname" value="SpringBoot"/>
</bean>
  1. 向集合中注入 Course 对象
<!--注入List<Course>类型,集合中放对象-->
<property name="courseList">
    <list>
        <ref bean="course1"/>
        <ref bean="course2"/>
        <ref bean="course3"/>
    </list>
</property>

把集合注入部分提取出来

在集合里面设置对象类型值只能在内部使用,把集合注入部分提取出来可以公共使用

  1. 在Spring配置文件中引入名称空间 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/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">
  1. 使用 uitl标签 完成list集合注入提取
<util:list id="courseList">
    <ref bean="course1"/>
    <ref bean="course2"/>
    <ref bean="course3"/>
</util:list>

<bean id="course1" class="com.example.spring.collectiontype.Course">
    <property name="cname" value="Spring"/>
</bean>
<bean id="course2" class="com.example.spring.collectiontype.Course">
    <property name="cname" value="Mybatis"/>
</bean>
<bean id="course3" class="com.example.spring.collectiontype.Course">
    <property name="cname" value="SpringBoot"/>
</bean>
  1. list集合注入
<!--注入List<Course>类型,集合中放对象-->
<property name="courseList" ref="courseList"/>

测试

image-20220515122157704

3.7 Bean管理(FactoryBean)

Spring有两种bean,一种普通bean,另外一种工厂bean(FactoryBean)

普通bean:

在配置文件中定义bean的类型就是它的返回类型

<bean id="student" class="com.example.spring.collectiontype.Student">

</bean>
Student student = context.getBean("student", Student.class);

工厂bean:

作用:

一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。

FactoryBean 通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。

在配置文件中定义的bean类型可以和返回类型不一样

  1. 第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean

  2. 第二步:实现接口里面的方法,在实现的方法中定义返回的bean类型

public class MyBean implements FactoryBean<Course> {
    // 定义返回bean
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("创造对象的工厂");
        return course;
    }

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

    public boolean isSingleton() {
        return false;
    }
}
  1. 第三步:在配置文件中,定义返回类型就是工厂的类
<bean id="myBean" class="com.example.spring.factory.MyBean"/>

测试

@Test
public void test02(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    Course course = context.getBean("myBean", Course.class);
    System.out.println("course = " + course);
}

image-20220515145713584

3.8 Bean管理(bean作用域)

  1. 在Spring里面,默认情况下,bean是单实例对象
@Test
public void test01(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Student student1 = context.getBean("student", Student.class);
    Student student2 = context.getBean("student", Student.class);
    System.out.println("student1 = " + student1);
    System.out.println("student2 = " + student2);
}

image-20220515150405022

  1. 在Spring中可以设置是单实例或者多实例

在Spring配置文件bean标签里面有属性:scope,用于设置单实例还是多实例

scope属性值:

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

区别:

  • 单实例是IOC容器启动时就会去实例化bean并添加到容器当中去,每次获取都是从容器中获取同一个对象
  • 多实例是去获取对象的时候才回去实例化bean,每次获取都会去实例化bean

使用工厂bean生成的对象也是多实例的

为什么用单例、多例:

之所以用单例,是因为没必要每个请求都新建一个对象,这样子既浪费CPU又浪费内存;

之所以用多例,是为了防止并发问题;即一个请求改变了对象的状态,此时对象又处理另一个请求,而之前请求对对象状态的改变导致了对象对另一个请求做了错误的处理;

用单例和多例的标准只有一个:

当对象含有可改变的状态时(更精确的说就是在实际应用中该状态会改变),用多例,否则单例;

3.9 Bean管理(bean生命周期)

什么是生命周期:

从对象创建到对象销毁的过程

bean的生命周期

  1. 通过构造器创建bean实例(无参构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. 调用bean的初始化的方法(需要进行配置初始化时执行的方法)
  4. bean可以使用了(对象获取到了)
  5. 当容器关闭的时候,调用bean的销毁的方法(需要配置销毁时执行的方法)

演示

  1. 创建演示实体类
public class Orders {
    private String oname;

    public Orders() {
        System.out.println("第一步:无参构造");
    }

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步:调用set方法");
    }

    public void initMethod(){
        System.out.println("第三步:初始化实例");
    }

    public void destroyMethod(){
        System.out.println("第五步:销毁实例");
    }
}
  1. 配置文件
<!--
    init-method:初始化时执行的方法
    destroy-method:销毁实例时执行的方法
-->
<bean id="order" class="com.example.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="鸡腿"/>
</bean>
  1. 测试
@Test
public void test03() {
    // 使用Application的实现类
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    Orders order = context.getBean("order", Orders.class);
    System.out.println("第四步:bean可以使用了");
    System.out.println(order);

    // 手动销毁bean实例
    context.close();
}
  1. 演示情况

image-20220515162714135

bean的后置处理器

加上后置处理器的话,bean的生命周期有7步

  1. 通过构造器创建bean实例(无参构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. 后置处理器的前置方法,把bean实例传递bean后置处理器
  4. 调用bean的初始化的方法(需要进行配置初始化时执行的方法)
  5. 后置处理器的执行方法,传递bean
  6. bean可以使用了(对象获取到了)
  7. 当容器关闭的时候,调用bean的销毁的方法(需要配置销毁时执行的方法)

后置处理器演示

  1. 创建类,实现接口 BeanPostProcessor,并重写两个方法。
public class MyBeanPost implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器的前置方法");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置处理器的后置方法");
        return bean;
    }
}
  1. 修改配置文件
<!--
    init-method:初始化时执行的方法
    destroy-method:销毁实例时执行的方法
-->
<bean id="order" class="com.example.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="鸡腿"/>
    
</bean>

<!--
    配置后置处理器
    当实现了BeanPostProcessor接口,spring就会知道这是个后置处理器,会为当前配置文件中的所有bean都配置上后置处理器。
-->
<bean id="myBeanPost" class="com.example.spring.bean.MyBeanPost"/>
  1. 测试查看效果

image-20220515165627686

3.10 Bean管理(xml自动装配)

什么是自动装配

根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入。

演示

  1. 创建两个类,emp中使用Dept对象属性
@Data
public class Emp {
    private Dept dept;
}
@Data
public class Dept {

}
  1. 在配置文件中设置自动装配
<!--
    autowire:自动装配
    byName:根据名称来自动装配,配置文件中bean的id要和实体类中属性名称相同
    byType:根据属性类型注入,若有两个相同类型的bean,就无法自动注入了
-->
<bean id="emp" class="com.example.spring.EmpDept.Emp" autowire="byName">

</bean>
<bean id="dept" class="com.example.spring.EmpDept.Dept"/>
  1. 测试查看效果
@Test
public void test04() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    Emp emp = context.getBean("emp", Emp.class);
    System.out.println(emp);
}

image-20220515171349349

基于xml的自动装配使用地极少,基本是用注解是方式来自动装配。

3.11 Bean管理(外部属性文件)

比如说,一个类中的属性很多,那么配置文件中就要写非常多的 property 标签,并不是很方便,特别是有变化的时候,还需要修改配置文件,而这个文件中很可能还有其他配置,所以改起来不方便。

所以可以把一些固定值或一些相关的值放到一个 properties 文件中,然后把这个文件引入到xml配置文件中(类似Mybatis的数据库信息)。

  1. 直接配置连接池的方式,各种信息都是写死的
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/harmonyos?serverTimezone=%2B8"/>
    <property name="username" value="root"/>
    <property name="password" value="admin"/>
</bean>
  1. 引入外部属性文件(properties文件)

新建properties文件,写好jdbc信息。

image-20220515211436259
引入context命名空间

<!--引入context-->
<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">

在配置文件中引入properties配置文件

<context:property-placeholder location="jdbc.properties"/>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

3.12 Bean管理(基于注解方式)

什么是注解

  • 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值,...)
  • 使用注解,注解作用在类上面,方法上面,属性上面
  • 使用注解目的:简化xml配置

Spring针对Bean管理中创建对象提供注解

  • @Component:普通注解,都可以用它创建对象
  • @Service:一般用在业务逻辑层(Service层)
  • @Controller:一般用在Web层
  • @Repository:一般用在DAO层

这四个注解功能是一样的,都可以用来创建bean实例。

这几个注解并没有规定一定要放哪一层,只是为了方便开发,但是最好是建议哪层放哪层

基于注解方式实现创建对象(⭐⭐⭐⭐⭐)

  1. 引入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.18</version>
</dependency>
  1. 开启组件扫描(要先引入context命名空间)
<?xml version="1.0" encoding="UTF-8"?>
<!--引入context命名空间-->
<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">
    <!--开启组件扫描,找到包中的所有类,如果类上有相关的注解,就把相应对象创建
        1.  如果扫描多个包,多个包使用逗号隔开
        2.  扫描多个包,可以直接扫描它的上层目录
    -->
    <context:component-scan base-package="com.example.spring"/>
</beans>
  1. 创建类
// 注解内的value属性可以省略不写,默认值是类名称,首字母小写。UserService——userService
// 四个注解都可以
//@Component(value = "userService")   //@Component("userService")
@Service
public class UserService {
    public void add() {
        System.out.println("userService .....");
    }
}
  1. 测试
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    // 这个bean的名称就是注解里的value,或者默认是类名,首字母小写
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println("userService = " + userService);
    userService.add();
}

image-20220515220315877

组件扫描配置

<!--示例1
        use-default-filters="false",表示现在不适用默认filter,自己配置filter
        context:include-filter,设置扫描哪些内容
    -->
<context:component-scan base-package="com.example" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!--示例2
        下面配置扫描包所有内容
        context:exclude-filter,设置哪些内容不进行扫描
    -->
<context:component-scan base-package="com.example">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

基于注解方式实现属性注入(⭐⭐⭐⭐⭐)

常用注解:

  • @Autowired:根据属性类型自动注入(自动装配)
  • @Qualifier:根据属性名称进行注入
  • @Resource:可以根据类型注入,也可以根据名称注入
  • @Value:注入普通类型属性
@Autowired
  1. 创建 UserDao 接口
public interface UserDao {
    void add();
}
  1. 创建类,实现 UserDao 接口,并在实现类上添加创建对象注解
@Repository
public class UserDaoImpl implements UserDao{

    public void add() {
        System.out.println("UserDao add .....");
    }
}
  1. UserService注入UserDao对象,在service类添加dao类型属性,在属性上面使用注解。
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void add() {
        System.out.println("userService add .....");
        userDao.add();
    }
}

spring 4.0开始就不推荐使用属性注入(@Autowired),改为推荐构造器注入和setter注入。

  1. 测试
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    // 这个bean的名称就是注解里的value,或者默认是类名,首字母小写
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println("userService = " + userService);
    userService.add();
}

image-20220515223727412

@Qualifier

若一个接口有多个实现类,使用@Autowired就会不知道要注入哪个实现类,所以使用根据名称注入

@Qualifier注解要和@Autowired注解一起使用

  1. 再创建一个类,实现 UserDao 接口,UserDaoImpl2
@Repository(value = "userDaoImpl2")
public class UserDaoImpl2 implements UserDao{

    public void add() {
        System.out.println("UserDaoImpl2 add ......");
    }
}
  1. 使用@Qualifier注解
@Service
public class UserService {
    // 两个注解要一起使用,@Qualifier要指定名称
    @Autowired
    @Qualifier(value = "userDaoImpl2")
    private UserDao userDao;

    public void add() {
        System.out.println("userService add .....");
        userDao.add();
    }
}
  1. 测试
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    // 这个bean的名称就是注解里的value,或者默认是类名,首字母小写
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println("userService = " + userService);
    userService.add();
}

image-20220515232026145

@Resource

@Resource可以根据类型注入,也可以根据名称注入

@Service
public class UserService {
	// 根据类型注入
    @Resource
    private UserDao userDao;

    public void add() {
        System.out.println("userService add .....");
        userDao.add();
    }
}
@Service
public class UserService {
	// 根据名称注入
    @Resource(name = "userDaoImpl2")
    private UserDao userDao;

    public void add() {
        System.out.println("userService add .....");
        userDao.add();
    }
}

Spring官方不建议使用@Resource,因为@Resource不是Spring的。

image-20220515233252598

@value

注入普通类型属性

@Service
public class UserService {
    @Value(value = "abc")
	private String name;
}

纯注解开发

  1. 创建配置类,替代xml配置文件
// 标注这个一个Spring的配置类
@Configuration
// 组件扫描
@ComponentScan(basePackages = {"com.example.spring"})
public class SpringConfig {
    
}
  1. 编写测试类
@Test
public void test2(){
    // 加载配置类,和xml不同
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}
  1. 其余注解操作都一样了

参考资源:

B站尚硅谷:https://www.bilibili.com/video/BV1Vf4y127N5?p=24

posted @ 2022-05-15 23:53  KledKled  阅读(16)  评论(0编辑  收藏  举报