感觉注解和xml东西很多容易混,记录一下
文章目录
Ioc的概念
按照百度百科上的内容是:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
通俗的讲,就是将新建对象的控制权交给了spring,这就是控制反转。
Ioc的使用
创建对象
xml实现
id为名称,class是这个类的位置
例如我加载一个时间类
<bean id="time" class="java.util.Date"></bean>
注解实现
在实体类之前加注解,作用与在xml配置文件中编写标签实现的功能一致
@Component
作用:
用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id,不写默认是当前类名,且首字母小写
例如:
@Component(value = "accountServiceImpl")
public class AccountServiceImpl implements AccountService {
public void saveAccount() {
System.out.println("saveAccount方法执行了...");
}
}
@Controller
:一般用在表现层
@Service
:一般用在业务层
@Repository
:一般用在持久层
这三个注解的作用和属性与@Component是一模一样的,这是spring框架提供的明确的三层使用的注解,使三层对象更加清晰
注入数据
通常创建对象采用的是默认无参构造方法
如果要携带数据的创建对象,维护依赖关系就是依赖注入
依赖注入:
能注入的数据有三类:
1.基本类型和String
2.其他bean类型(在配置文件中或者注解配置过的bean)
3.复杂类型/集合类型
用于给List结构集合注入的标签:
list array set
用于给Map结构集合注入的标签:
map props
结构相同,标签可以互换
常用的注入方式的有三种:
1.使用构造函数提供
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
type
:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index
:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从0开始
name
:用于指定给构造函数中指定名称的参数赋值(常用)
value
:用于提供基本类型和String类型的数据
ref
:用于指定其他的bean类型数据,它指的是在spring的Ioc核心容器中出现过的bean对象
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
例子:
一个实体类,在实体类中存放了三个属性和有参构造函数
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("saveAccount方法执行了..."+name+","+age+","+birthday+"");
}
}
构造函数注入
<bean id="accountService" class="service.impl.AccountServiceImpl">
<constructor-arg name="name" value="Ray"></constructor-arg>
<constructor-arg name="age" value="22"></constructor-arg>
<constructor-arg name="birthday" ref="time"></constructor-arg>
</bean>
<bean id="time" class="java.util.Date"></bean>
2.使用set方法提供
涉及的标签:property
出现的位置:bean标签的内部
标签的属性:
name
:用于指定注入时所调用的set方法名称
value
:用于提供基本类型和String类型的数据
ref
:用于指定其他的bean类型数据,它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:如果有某个成员必须有值,则获取对象时有可能set方法没有执行
例子:
一个实体类,在实体类中存放了三个属性和三个set方法
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("saveAccount方法执行了..."+name+","+age+","+birthday+"");
}
}
set方法注入
<bean id="accountService" class="service.impl.AccountServiceImpl">
<property name="name" value="Ray"></property>
<property name="age" value="22"></property>
<property name="birthday" ref="time"></property>
</bean>
<bean id="time" class="java.util.Date"></bean>
3.使用注解提供
作用与在xml配置文件中的bean标签中写一个property标签的作用一致
@Autowired
作用:自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配就可以注入成功。
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果ioc容器有多个类型匹配时,就会查找这个类型下,实体类变量名和注解id一致的唯一一个,否则就报错。
出现位置:可以是变量上,也可以是方法上
细节:在使用注解注入时,set方法就不是必须的了
@Qualifier
作用:在按照类中注入的基础之上再按照名称注入,它在给类成员注入时不能单独使用,需要配合@Autowired
@Resource
(推荐)
作用:直接按照bean的id注入,它可以独立使用
属性:
name:用于指定bean的id
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能通过XML来实现。
@Value
作用:用于注入基本类型和String类型的数据
属性:
value:用于指定数据的值,它可以使用spring中的SpEL(也就是spring的EL表达式)
SpEL的写法:${表达式}
改变作用范围
scope的参数有:
1.singleton
单例模式
2.prototype
多例模式
关于单例模式,我的理解是在对象创建后,工厂会保存这个对象,使下次调用该对象时可复用
关于多例模式,我的理解是不会保存对象,每次调用都新建对象使用
在spring中,智能的对单例模式采用了立即加载,对多例模式采用了懒加载。
立即加载就是在对象创建时把所有东西都加载进来
懒加载是在需要对象的这些东西时再加载进来
生命周期相关
1.init-method
2.destroy-method
单例对象
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
Spirng整合Junit的配置
1.导入spring整合junit的jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2.使用Junit提供的注解把原有的main方法替换,替换成spring提供的
@Runwith
3.告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
@ContextConfiguration
Location:指定xml文件的位置,加上classpath关键字,表示在类路径下
classes:指定注解类所在地位置
当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
案例
目的:在对数据库的操作中使用Ioc
使用xml实现
文件路径树如下:
pom.xml,导入需要的依赖坐标
<?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>org.example</groupId>
<artifactId>SpringDEMO_annotation</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
AccountDao接口
package dao;
import domain.Account;
import java.util.List;
/**
* 账户的持久层接口
*/
public interface AccountDao {
/**
* 查询所有
* @return
*/
List<Account> findAllAccount();
/**
* 查询一个
* @return
*/
Account findAccountById(Integer accountId);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
* 更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
* @param accountId
*/
void deleteAccount(Integer accountId);
}
AccountDaoImpl,接口实现类
package dao.impl;
import dao.AccountDao;
import domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.stereotype.Component;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List<Account> findAllAccount() {
try{
return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
public Account findAccountById(Integer accountId) {
try{
return runner.query("select * from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try{
runner.update("insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try{
runner.update("update account set name=?,money=? where id=?",new BeanListHandler<Account>(Account.class),account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void deleteAccount(Integer accountId) {
try{
runner.update("delete from account where id=?",accountId);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
Account类,对数据进行封装
package domain;
import java.io.Serializable;
/**
* 账户实体类
*/
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
AccountService接口
package service;
import domain.Account;
import java.util.List;
/**
* 账户的业务层接口
*/
public interface AccountService {
/**
* 查询所有
* @return
*/
List<Account> findAllAccount();
/**
* 查询一个
* @return
*/
Account findAccountById(Integer accountId);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
* 更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
* @param accountId
*/
void deleteAccount(Integer accountId);
}
AccountServiceImpl实体类
package service.impl;
import dao.AccountDao;
import domain.Account;
import service.AccountService;
import java.util.List;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao){
this.accountDao=accountDao;
}
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer accountId) {
accountDao.deleteAccount(accountId);
}
}
bean.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"
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">
<!-- 配置Service -->
<bean id="accountService" class="service.impl.AccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置Dao对象 -->
<bean id="accountDao" class="dao.impl.AccountDaoImpl">
<property name="runner" ref="runner"></property>
</bean>
<!-- 配置QueryRunner对象 -->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 注入数据源 -->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_try"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
测试类,AccountServicetest
package test;
import domain.Account;
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 service.AccountService;
import java.util.List;
/**
* 使用junit单元测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServicetest {
@Autowired
private AccountService as;
@Test
public void testFindAll(){
//3.执行方法
List<Account> accounts=as.findAllAccount();
for(Account account:accounts){
System.out.println(account);
}
}
@Test
public void testFindOne(){
//3.执行方法
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave(){
//3.执行方法
Account account=new Account();
account.setName("Ray");
account.setMoney(9999f);
as.saveAccount(account);
}
}
使用注解实现
文件路径树如下:
相同的三层架构就不再放源码
SpringConfiguration的java文件,作为父配置文件来套子配置文件
package itRay.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
/**
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring中的新注释
* Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写
* ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包
* 我们使用此注释就等同于在xml配置了
* <context:component-scan base-package="itRay"></context:component-scan>
* Bean
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
* 属性:
* name:用于指定bean的id,当不写时,默认值是当前方法的名称
* 细节:
* 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
* 查找的方式和Autowired注解的作用是一样的
*
* Import
* 作用:用于导入其他的配置类
* 属性:
* value:用于指定其他配置类的字节码
* 当我们使用Import的注解之后,有Import注解的类就是父配置类,而导入的都是子配置类
* PropertySource
* 作用:用于指定properties文件的位置
* 属性:
* value:指定文件的名称和路径
* 关键字:classpath,表示类路径下
*/
@Configuration
@ComponentScan(basePackages = {"itRay"})
@Import(JdbcConfig.class)
@PropertySource(value = {"classpath:JdbcConfig.properties"})
public class SpringConfiguration {
}
JdbcConfig的java文件,这里调用了properties配置文件来动态加载数据库驱动的信息
package itRay.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 用于创建一个QueryRunner对象
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope(scopeName = "prototype")
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
* @return
*/
@Bean(name = "dataSource")
public DataSource createDataSource(){
try{
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
AccountServiceImpl实体类
package itRay.service.impl;
import itRay.dao.AccountDao;
import itRay.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import itRay.service.AccountService;
import java.util.List;
@Service(value = "accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer accountId) {
accountDao.deleteAccount(accountId);
}
}
AccountDaoImpl实体类
package test;
import itRay.config.SpringConfiguration;
import itRay.domain.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import itRay.service.AccountService;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* 使用junit单元测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServicetest {
@Autowired
private AccountService as=null;
@Test
public void testFindAll(){
//3.执行方法
List<Account> accounts=as.findAllAccount();
for(Account account:accounts){
System.out.println(account);
}
}
@Test
public void testFindOne(){
//3.执行方法
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave(){
//3.执行方法
Account account=new Account();
account.setName("Ray");
account.setMoney(9999f);
as.saveAccount(account);
}
@Test
public void testUpdate(){
}
@Test
public void testDelete(){
}
}