学习链接: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
配置文件
编写测试类查看对象地址
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);
}
}
地址一样代表是同一个对象
2)prototype
applicationContext.xml配置文件
输出结果显示地址不一样,代表有多个对象
-
当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)
运行单元测试:
这里为什么没有运行destory方法呢?原因是在单元测试中,还没有来得及打印,方法就执行完毕了。
怎么测试呢?我们可以手动关闭容器。(简单了解 )
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>
- 工厂实例方法实例化
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>
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
运行测试:
- 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
运行测试:
3)p命名空间注入
p命名空间注入本质也是set方法注入,但比起上述set方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入p命名空间:
xmlns:p="http://www.springframework.org/schema/p"
其次,需要修改注入方式
<bean id="userDao" class="com.abner.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.abner.service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean>
运行测试:
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给属性赋值
运行测试:
- 引用数据类型(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>
运行测试:
1.8 引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
<import resource="applicationContext-xxx.xml"/>
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运行结果
test2运行结果
3)抽取配置文件
在test目录下创建resources文件夹https://blog.csdn.net/qq_38737992/article/details/92630443
在resources文件夹下创建jdbc.properties文件
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
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
测试:
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包用于示例
在原来的注入方式中我们使用配置文件方式注入
使用注解代替
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();
}
}
我们也可以用@Repository和@Service代替dao层和service层的Component,使得一眼能看出这个类是在哪个层。
而且在用@Autowired和@Qualifier注入属性时,set方法也可以不写
在容器中的相同类型的Bean只有一个时@Qualifier也可以不写,因为@Autowired是按照数据类型从Spring容器中进行匹配
当容器中相同类型的Bean不止一个时,就要用@Qualifier用id进行匹配,但@Qualifier要结合@Autowired一起使用
当然也可以使用@Resource(name = “要注入的Bean的id”)代替@Qualifier+@Autowired
普通属性注入使用@Value(“属性值”):
@Value("itcast")
private String dirver;
虽然这样的用处不大,还不如String driver = “itcast”,但是我们可以通过获得properties文件中key对应的进行赋值,这样用处就很大
我们可以用@Scope标注Bean的作用范围,这里就不做演示,和之前的效果一样
初始化方法和销毁方法的注解
@PostConstruct
public void init(){
System.out.println("Service对象的初始化方法");
}
@PreDestroy
public void destory(){
System.out.println("Service对象的销毁方法");
}
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();
}
}
运行测试:
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());
}
}