spring学习笔记

学习链接:https://www.bilibili.com/video/BV1WZ4y1P7Bp

1、Spring 配置文件

用于配置对象交由Spring来创建。
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功

在resources文件夹下创建,名字随意起,不过约定俗成为applicationContext.xml

1.1 Bean标签基本属性

  • id:Bean实例在Spring容器中的唯一标识
  • class:Bean的全限定名称(包.类)
  • <bean id=“userDao” class=“com.abner.dao.impl.UserDaoImpl”></bean>

1.2 Bean标签范围配置

scope: 指对象的作用范围

取值范围 说明
singleton 默认的,单利的,即容器中存在的对象只有一个
prototype 多例的,即容器中的bean对象有多个 ,每次调用getBean()方法时都会给你一个新的对象
request WEB项目中,Spring创建一个Bean对象,将对象存入到request域中
session WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
global session WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession相当于session

<bean scope=“singleton”></bean>
<bean scope="prototype "></bean>

scope 属性测试

1)singleton
配置文件
spring学习笔记
编写测试类查看对象地址

public class SpringTest {

    @Test
    //测试scope属性
    public void test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao1 = (UserDao) app.getBean("userDao");
        UserDao userDao2 = (UserDao) app.getBean("userDao");

        System.out.println(userDao1);
        System.out.println(userDao2);

    }

}

地址一样代表是同一个对象
spring学习笔记
2)prototype
applicationContext.xml配置文件
spring学习笔记
输出结果显示地址不一样,代表有多个对象
spring学习笔记

  • 当scope的取值为singleton时
    • Bean的实例化个数:1个
    • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
    • Bean的声明周期:
      • 对象创建:当应用加载,创建容器时,对象就被创建
      • 对象运行:只要容器在,对象一直活着
      • 对象销毁:但应用卸载,销毁容器时,对象就被销毁了
  • 当scope的取值为prototype时
    • bean的实例化个数:多个
    • bean的实例化时机:当调用getBean()方法时实例化Bean
    • bean的声明周期
      • 对象创建:当使用对象时,创建新的对象实例
      • 对象运行:只要对象在使用中,就一直活着
      • 对象销毁:当对象长时间不用时,被Java的垃圾回收器回收了

1.3 Bean生命周期配置

  • init-method:指定类中的初始化方法名称
  • destory-method:指定类中销毁方法名称

applicationContext.xml配置文件中声明

<bean id="userDao" class="com.abner.dao.impl.UserDaoImpl" init-method="init" destroy-method="destory"></bean>

在UserDaoImpl中创建了初始化方法和销毁方法(方法名可自定义,为了增加可读性,分别叫init和destory)

spring学习笔记
运行单元测试:
spring学习笔记
这里为什么没有运行destory方法呢?原因是在单元测试中,还没有来得及打印,方法就执行完毕了。
怎么测试呢?我们可以手动关闭容器。(简单了解 )
spring学习笔记

1.4 Bean实例化三种方式

  • 无参构造方法实例化(重点,由于上面的实例就是无参构造方法的,所以在此处不做演示)
  • 工厂静态方法实例化
    1)创建工厂类
package com.abner.factory;

import com.abner.dao.UserDao;
import com.abner.dao.impl.UserDaoImpl;

public class StaticFactory {

    public static UserDao getUserDao(){
        return new UserDaoImpl();
    }

}

2)在applicationContext.xml配置文件中声明

<!--用工厂静态方法实例化-->
<bean id="userDao" class="com.abner.factory.StaticFactory" factory-method="getUserDao"></bean>

spring学习笔记

  • 工厂实例方法实例化
    1)创建工厂类
package com.abner.factory;

import com.abner.dao.UserDao;
import com.abner.dao.impl.UserDaoImpl;

public class DynamicFactory {

    public UserDao getUserDao(){
        return new UserDaoImpl();
    }

}

2)在applicationContext.xml配置文件中声明

    <!--用工厂实例方法实例化-->
    <bean id="factory" class="com.abner.factory.DynamicFactory"></bean>
    <!--在factory这个工厂中的getUserDao方法获取UserDao对象-->
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>

spring学习笔记

1.5 Bean的依赖注入

依赖注入(Dependency Injection):它是Spring框架核心IOC的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。
IOC解耦只是降低了他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法

那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

1.6 Bean的依赖注入方式

怎么将UserDao怎样注入到UserService内部呢?

  • 有参构造方法构造方法注入
    1)UserServiceimpl类
package com.abner.service.impl;

import com.abner.dao.UserDao;
import com.abner.service.UserService;

public class UserServiceImpl implements UserService {


    private UserDao userDao;

    public UserServiceImpl() {
    }

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }


    @Override
    public void save() {
        userDao.save();
    }
}

2)applicationContext.xml配置文件中声明

<!--构造方法将bean依赖注入-->
<bean id="userDao" class="com.abner.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.abner.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

constructor-arg中name为构造方法的参数名(idea会有提示),ref为引用容器中的哪个bean,也就是配置文件中的要注入bean的id

运行测试:
spring学习笔记

  • set方法注入
    1)UserServiceimpl类
package com.abner.service.impl;

import com.abner.dao.UserDao;
import com.abner.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceImpl implements UserService {


    private UserDao userDao;

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

    @Override
    public void save() {
        userDao.save();
    }
}

2)applicationContext.xml配置文件中声明

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

property中name为你的set方法名去掉set并且把第一个字母变为小写(idea有提示),ref为引用容器中的哪个bean,也就是配置文件中的要注入bean的id

运行测试:
spring学习笔记
3)p命名空间注入
p命名空间注入本质也是set方法注入,但比起上述set方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入p命名空间:

xmlns:p="http://www.springframework.org/schema/p"

spring学习笔记
其次,需要修改注入方式

<bean id="userDao" class="com.abner.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.abner.service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean>

运行测试:
spring学习笔记

1.7 Bean的依赖注入的数据类型

上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型:

  • 普通数据类型(以UserDao举例)
    1)UserDaoImpl类
package com.abner.dao.impl;

import com.abner.dao.UserDao;

public class UserDaoImpl implements UserDao {

    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public void save() {
        System.out.println("save running.......");
        System.out.println(username+"==========="+age);
    }
}

UserServiceImpl类

package com.abner.service.impl;

import com.abner.dao.UserDao;
import com.abner.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceImpl implements UserService {

    @Override
    public void save() {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

2)applicationContext.xml配置文件中声明

<bean id="userService" class="com.abner.service.impl.UserServiceImpl"></bean>
<bean id="userDao" class="com.abner.dao.impl.UserDaoImpl">
    <property name="username" value="zhangsan"></property>
    <property name="age" value="18"></property>
</bean>

因为ref是代表对象的引用,普通属性值不能用,所以用value给属性赋值

运行测试:
spring学习笔记

  • 引用数据类型(1.6操作就是,这里不做演示)
  • 集合数据类型
    1)UserDaoImpl类
package com.abner.dao.impl;

import com.abner.dao.UserDao;
import com.abner.domain.User;

import java.util.List;
import java.util.Map;
import java.util.Properties;

public class UserDaoImpl implements UserDao {

    private List<String> strList;
    private Map<String, User> userMap;
    private Properties properties;


    public void setStrList(List<String> strList) {
        this.strList = strList;
    }

    public void setUserMap(Map<String, User> userMap) {
        this.userMap = userMap;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void save() {
        System.out.println(strList);
        System.out.println(userMap);
        System.out.println(properties);
        System.out.println(username+"==========="+age);
    }
}

UserServiceImpl类

package com.abner.service.impl;

import com.abner.dao.UserDao;
import com.abner.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceImpl implements UserService {

    @Override
    public void save() {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

2)applicationContext.xml配置文件中声明

 <!--集合数据类型注入-->
    <bean id="userService" class="com.abner.service.impl.UserServiceImpl"></bean>
    <bean id="userDao" class="com.abner.dao.impl.UserDaoImpl">
        <property name="strList" >
            <list>
                <!--如果strList中泛型为User这种数据类型或者其他引用对象类型,就用ref,普通类型用value-->
                <value>aaaa</value>
                <value>bbbb</value>
                <value>cccc</value>
            </list>
        </property>
        <property name="userMap">
            <map>
                <!--如果泛型为User这种数据类型或者其他引用对象类型,就用key-ref、value-ref,普通类型用key、value-->
                <entry key="u1" value-ref="user1"></entry>
                <entry key="u2" value-ref="user2"></entry>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="p1">pppp1</prop>
                <prop key="p2">pppp2</prop>
                <prop key="p3">pppp3</prop>
            </props>
        </property>
    </bean>
    <bean id="user1" class="com.abner.domain.User">
        <property name="name" value="tom"></property>
        <property name="addr" value="beijing"></property>
    </bean>
    <bean id="user2" class="com.abner.domain.User">
        <property name="name" value="lucy"></property>
        <property name="addr" value="tianjing"></property>
    </bean>

运行测试:
spring学习笔记

1.8 引入其他配置文件(分模块开发)

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载

<import resource="applicationContext-xxx.xml"/>

spring学习笔记

1.9 知识要点

Spring的重点配置

<bean>标签
	id属性:在容器中Bean实例的唯一标识,不允许重复
	class属性:要实例化的Bean的全限定名(全包名:包.包...包.类)
	scpoe属性:Bean的作用范围,常用的是Singleton(默认)和prototype
	<property>标签:属性注入
		name属性:属性名称
		value属性:注入的普通属性值
		ref属性:注入的对象引用值
		<list>标签
		<map>标签
		<properties>标签
	<constructor-arg>标签
<import>标签:导入其他的Spring的分文件

2、Spring相关API

2.1 ApplicationContext的实现类

  • 1)ClassPathXMLApplicationContex
    • 他是从类的根路径下加载配置文件推荐使用这种
 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
  • 2)FileSystemXMLApplicationContext
    • 他是从磁盘路径上加载配置文件,配置文件可以放在磁盘任意位置
ApplicationContext app = new FileSystemXmlApplicationContext("E:\\IDEAProject\\spting_ioc\\src\\main\\resources\\applicationContext.xml");
//右键点击配置文件copy Path获取磁盘路径
  • 3)AnnotationConfigApplicationContext
    • 当使用注解配置容器对象时,需要使用此类来创建spring容器,它用来读取注解

2.2 getBean()方法的使用

1、根据id获取,允许容器中可以出现多个相同类型的Bean(因为id具有唯一性,只要根据不同的id来获取)
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
比如,在配置文件中有如下两个类型相同的Bean

<bean id="user1" class="com.abner.domain.User">
        <property name="name" value="tom"></property>
        <property name="addr" value="beijing"></property>
    </bean>
<bean id="user2" class="com.abner.domain.User">
    <property name="name" value="lucy"></property>
    <property name="addr" value="tianjing"></property>
</bean>

那么我们就可以根据id的不同,而获取到不同的Bean

UserService userService = (UserService) app.getBean("userService");

2、根据字节码对象类型获取,Spring会按照类型在容器中匹配,所以不允许容器中存在多个相同类型的Bean,会报错,因为不能分清到底获取哪一个。
public T getBean(Class requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}

UserService userService = app.getBean(UserService.class);

2.3 知识要点

Spring的重点API

 ApplicationContext app = new ClassPathXmlApplicationContext("xml文件");
 app.getBean("id");//容器中相同类型的Bean存在多个用这种
 app.getBean(Class);//容器中相同类型的Bean只有一个用这个

3、Spring配置数据源

3.1 数据源(连接池)的作用

  • 数据源(连接池)就是提高程序性能而出现的
  • 事先实例化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕后将连接资源归还数据源

常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid

3.2 数据源的开发步骤

1)导入数据源的坐标和数据库驱动坐标
2)创建数据源对象
3)设置数据源的基本连接数据

  • 1、驱动(mysql、Oracle…等)
  • 2、数据库地址
  • 3、用户名
  • 4、密码

4)使用数据源获取连接资源和归还资源

3.3 数据源的手动创建

1)导入数据源的坐标和数据库驱动坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.abner</groupId>
    <artifactId>spring_ioc_anno</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>



</project>

2)、创建测试类

package com.abner.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;

import java.beans.PropertyVetoException;
import java.sql.Connection;

public class DataSourceTest {

    //测试手动创建 c3p0 数据源
    @Test
    public void test1() throws Exception {
        //创建数据源
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        //设置基本连接参数
        dataSource.setDriverClass("com.mysql.jdbc.Driver");//驱动
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/yuanshen");//数据库地址
        dataSource.setUser("root");//用户名
        dataSource.setPassword("root");//密码

        Connection connection = dataSource.getConnection();//获取连接

        System.out.println(connection);

        connection.close();

    }

    //测试手动创建 druid 数据源
    @Test
    public void test2() throws Exception {

        DruidDataSource dataSource = new DruidDataSource();

        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/yuanshen");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        DruidPooledConnection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();

    }

}

test1运行结果
spring学习笔记
test2运行结果
spring学习笔记

3)抽取配置文件

在test目录下创建resources文件夹https://blog.csdn.net/qq_38737992/article/details/92630443
在resources文件夹下创建jdbc.properties文件
spring学习笔记
jdbc.properties:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/yuanshen
jdbc.username=root
jdbc.password=root

测试类:

package com.abner.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.util.ResourceBundle;

public class DataSourceTest {

    //测试手动创建 c3p0 数据源(加载properties配置文件形式)
    @Test
    public void test3() throws Exception {
        //读取配置文件
        /**
         * baseName:相对于类加载路径(resources)地址下
         * 的properties的基本名称,也就是去掉后缀的名称
         * 如果你的properties文件叫abc.properties,那么就填abc
         */
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");//
        //根据配置文件的key获取对应的值
        String driver = rb.getString("jdbc.driver");
        String url = rb.getString("jdbc.url");
        String username = rb.getString("jdbc.username");
        String password = rb.getString("jdbc.password");

        //创建数据源对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        //设置基本连接参数
        dataSource.setDriverClass(driver);//驱动
        dataSource.setJdbcUrl(url);//数据库地址
        dataSource.setUser(username);//用户名
        dataSource.setPassword(password);//密码

        Connection connection = dataSource.getConnection();//获取连接

        System.out.println(connection);

        connection.close();


    }

    //测试手动创建 druid 数据源(加载properties配置文件形式)
    @Test
    public void test4() throws Exception {

        //读取配置文件
        /**
         * baseName:相对于类加载路径(resources)地址下
         * 的properties的基本名称,也就是去掉后缀的名称
         * 如果你的properties文件叫abc.properties,那么就填abc
         */
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");
        //根据配置文件的key获取对应的值
        String driver = rb.getString("jdbc.driver");
        String url = rb.getString("jdbc.url");
        String username = rb.getString("jdbc.username");
        String password = rb.getString("jdbc.password");

        DruidDataSource dataSource = new DruidDataSource();

        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        DruidPooledConnection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();

    }

}

3.4 Spring配置数据源

可以将DataSource的创建权交由Spring容器去完成
在上面的测试中可以看出,c3p0和druid对象都有set方法,所以我们可以使用set方式注入依赖,使得Spring帮助我们创建并初始化数据源对象
resources下创建applicationContext.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">

    <!--c3p0数据源-->
    <bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/yuanshen"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!--druid数据源-->
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/yuanshen"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>


</beans>

测试类:

package com.abner.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ResourceBundle;

public class DataSourceTest {

    //测试spring容器产生数据源对象
    @Test
    public void test5() throws Exception{

        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

//        ComboPooledDataSource dataSource = (ComboPooledDataSource) app.getBean("dataSource1");

//        DataSource dataSource = app.getBean(DataSource.class);
        ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);

        Connection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();

    }

    @Test
    public void test6() throws Exception {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

        DruidDataSource dataSource = app.getBean(DruidDataSource.class);

        DruidPooledConnection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();
    }

}

3.5 抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件获得连接信息。

首先,需要引入context命名空间和约束路径:

  • 命名空间:xmlns:context="http://www.springframework.org/schema/context"
  • 约束路径:http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    spring学习笔记
    xml配置文件:
    <!--通过外部properties文件中的key获取对应的value注入-->
    <!--加载外部properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

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

${properties中的key名}
https://blog.csdn.net/fushuaiCSDN/article/details/107345158
测试:
spring学习笔记

3.6 知识要点

spring容器加载properties文件

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

<property name="" value="${key}"></property>

4、Spring注解开发

4.1 Spring原始注解(出现比较早的注解)

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。

  • Spring原始注解主要是替代<Bean>标签的配置
注解 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在Web层类上用于实例化Bean(和@Component功能一样,只不过具有语义化功能,让人看到就知道该类在Web层)
@Service 使用在Service层类上用于实例化Bean和@Component功能一样,只不过具有语义化功能,让人看到就知道该类在Service层)
@Repository 使用在dao层类上用于实例化Bean和@Component功能一样,只不过具有语义化功能,让人看到就知道该类在dao层)
@Autowired 使用在字段上用于根据数据类型依赖注入(替代property属性注入中的ref)
@Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入(替代property属性注入中的ref)
@Resource 相当于@Autowired+@Qualifier,按照名称进行注入(替代property属性注入中的ref)
@Value 注入普通属性(替代property属性注入中的value)
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestory 使用在方法上标注该方法是Bean的销毁方法

注意
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其报下的Bean需要进行扫描以便于识别使用注解配置类、字段和方法。

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

配置后spring会扫描base-package包下及其所有子包下的所有Bean

示例:
新建service、dao、web包用于示例
spring学习笔记
在原来的注入方式中我们使用配置文件方式注入
spring学习笔记
使用注解代替
userDaoImpl类

package com.abner.dao.impl;

import com.abner.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

//<bean id="userDao" class="com.abner.dao.impl.UserDaoImpl"></bean>
//@Component("id")
@Component("userDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void save() {
        System.out.println("save running........");
    }

}

UserServiceImpl类:

package com.abner.service.impl;

import com.abner.dao.UserDao;
import com.abner.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

//<bean id="userService" class="com.abner.service.impl.UserServiceImpl"></bean>
@Component("userService")
public class UserServiceImpl implements UserService {

//    <property name="userDao" ref="userDao"></property>
    @Autowired
    @Qualifier("userDao")//@Qualifier("要被注入的Bean的id")
    private UserDao userDao;

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

    @Override
    public void save() {
        userDao.save();
    }

}

UserController类:

package com.abner.web;

import com.abner.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserController {


    public static void main(String[] args){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//        UserService userService = (UserService) app.getBean("userService");
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }

}

spring学习笔记
我们也可以用@Repository和@Service代替dao层和service层的Component,使得一眼能看出这个类是在哪个层。
spring学习笔记
spring学习笔记
而且在用@Autowired和@Qualifier注入属性时,set方法也可以不写
spring学习笔记
在容器中的相同类型的Bean只有一个时@Qualifier也可以不写,因为@Autowired是按照数据类型从Spring容器中进行匹配
spring学习笔记
当容器中相同类型的Bean不止一个时,就要用@Qualifier用id进行匹配,但@Qualifier要结合@Autowired一起使用

当然也可以使用@Resource(name = “要注入的Bean的id”)代替@Qualifier+@Autowired
spring学习笔记
普通属性注入使用@Value(“属性值”):

    @Value("itcast")
    private String dirver;

spring学习笔记
虽然这样的用处不大,还不如String driver = “itcast”,但是我们可以通过获得properties文件中key对应的进行赋值,这样用处就很大
spring学习笔记
我们可以用@Scope标注Bean的作用范围,这里就不做演示,和之前的效果一样
spring学习笔记
初始化方法和销毁方法的注解

 @PostConstruct
    public void init(){
        System.out.println("Service对象的初始化方法");
    }

    @PreDestroy
    public void destory(){
        System.out.println("Service对象的销毁方法");
    }

spring学习笔记

4.2 Spring新注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:

  • 非自定义的Bean的配置:<bean>
  • 加载properties文件的配置:<context:property-placeholder>
  • 组件扫描:<context:component-scan>
  • 引入其他文件:<import>
注解 说明
@Configuration 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定Spring在初始化容器时要扫描的包。作用和在Spring的xml配置文件中的 <context:component-scan base-package=“com.abner”/> 一样
@Bean 在使用方法上,标注将该方法的返回值存储到Spring容器中
@PropertySource 用于加载 .properties 文件中的配置
@import 用于导入其他配置类

代码实现用注解完全替代xml配置文件
思想:创建一个核心配置类替代xml文件SpringConfiguration
先创建一个核心配置文件类,为了防止核心配置类过于臃肿,我们把各种配置分开成不同的配置类让后通过注解@Import(xxx.class)引入其他的配置类

package com.abner.config;

import org.springframework.context.annotation.*;

//标志该类是Spring的核心配置类
@Configuration
//<!--配置组件扫描-->
//<context:component-scan base-package="com.abner"></context:component-scan>
@ComponentScan("com.abner")
//<import resource=""/>
@Import(DataSourceConfiguration.class)//如果有多个@Import((DataSourceConfiguration.class,xxx.class...))
public class SpringConfiguration {

}

@Configuration 标志该类是Spring的核心配置类,相当于这是一个配置文件。
@ComponentScan(“com.abner”) 的作用和配置文件中的、<context:component-scan base-package=“com.abner”></context:component-scan>作用一样,配置组件扫描,这样就能扫描出用注解配置的Bean。
@Import(DataSourceConfiguration.class) 引入其他配置类相当于配置文件中的<import resource=“applicationContext-xxx.xml”/>

package com.abner.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

//<!--加载外部properties文件-->
//<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource") //Spring当前方法的返回值以指定名称存储到Spring容器中
    //返回值为你想放到容器中的Bean的类型
    public DataSource getDataSource() throws Exception {
        //创建数据源
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        //设置基本连接参数
        dataSource.setDriverClass(driver);//驱动
        dataSource.setJdbcUrl(url);//数据库地址
        dataSource.setUser(username);//用户名
        dataSource.setPassword(password);//密码

        return dataSource;
    }

}

@PropertySource加载外部properties文件,作用和<context:property-placeholder location=“classpath:jdbc.properties”></context:property-placeholder>一样

对于一些我们自定义的Bean我们可以直接通过注解 @Component、@Controller、@Service、@Repository 等把Bean放入Spring容器中,但是对于一些第三方包的Bean,例如ComboPooledDataSource这些外部数据源,我们不可以直接把注解添加到这些类中,因为第三方的,无法修改,所以我们要通过 @Bean 这个注解获取该类类型的返回值作为一个Bean存储到容器中。==@Bean(“dataSource”) == Spring把当前方法的返回值以指定名称存储到Spring容器中。名称可以不写,只要你return的值类型在容器中只有一个。
测试:

 @Test
    public void test7() throws Exception {
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        ComboPooledDataSource dataSource = app.getBean(ComboPooledDataSource.class);

        Connection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();
    }

    @Test
    public void test8() throws Exception {
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        DruidDataSource dataSource = app.getBean(DruidDataSource.class);

        DruidPooledConnection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();
    }

测试时我们只需要把ApplicationContext app = new ClassPathXmlApplicationContext(“applicationContext.xml”); 换成ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class); 即可:

package com.abner.web;

import com.abner.config.SpringConfiguration;
import com.abner.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserController {


    public static void main(String[] args){
//        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//        UserService userService = (UserService) app.getBean("userService");
        UserService userService = app.getBean(UserService.class);
        userService.save();

//        app.close();
    }

}

运行测试:
spring学习笔记

5、Spring继承Junit

5.1 原始Junit测试Spring的问题

在测试类中,每个测试方法都有以下两行代码:

ApplicationContext ac = new AnnotationConfigApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

5.2 上述问题的解决思路

  • 让SpringJunit负责创建Spring容器,但需要将配置文件的名称告诉它
  • 将需要进行测试的Bean直接在测试类中进行注入

5.3 Spring集成Junit步骤

1)导入Spring集成Junit坐标

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-test</artifactId>
     <version>5.0.5.RELEASE</version>
</dependency>

2)使用 @Runwith 注解替代原来的运行期
3)使用 @ContextConfiguration 指定配置文件或者配置类
4)使用 @Autowired 注入需要测试的对象
5)创建测试方法进行测试

package com.abner.test;

import com.abner.config.SpringConfiguration;
import com.abner.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")//@ContextConfiguration(value = "classpath:applicationContext.xml")value=可不写
@ContextConfiguration(classes = {SpringConfiguration.class})//全注解方式用这个
public class SpringJunitTest {

    @Autowired
    private UserService userService;

    @Resource(name = "dataSource1")
    private DataSource dataSource;

    @Test
    public void test1() throws SQLException {
        userService.save();
        System.out.println(dataSource.getConnection());
    }
}

上一篇:SpringBoot动态从数据库中获取数据源,动态切换数据源


下一篇:Spring第二章