学习基于注解的IOC配置,大家脑海里首先得有一个认知,即注解配置和xml要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
首先创建模板如图的测试格式:
@Component,@Repository,@Service,@Controller
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在Web层类上用于实例化Bean |
@Service | 使用在Service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
注意:
使用注解进行开发时,需要在application.xml中配置组件扫描,作用是指定哪个包及七子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法
示例代码:
<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-3.0.xsd">
<context:component-scan base-package="com.etime">context:component-scan>
beans>
使用@Component或者@Repository标识StudentDaoImpl需要对其进行Spring 实例化。
示例代码:
package com.etime.dao;
public interface StudentDao {
void show();
}
package com.etime.dao.Impl;
import com.etime.dao.StudentDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
//@Component("sd")
@Repository("sd")//@Component和@Component都能够达到相同的效果,但是@Repository只能实例对象dao层
public class StudentDaoImpl implements StudentDao {
@Override
public void show() {
System.out.println("这是studentDaoImpl的方法");
}
}
使用@Component或者@Service标识StudentServiceImpl需要Spring进行实例化
示例代码:
package com.etime.service;
public interface StudentService {
void show();
}
package com.etime.service.Impl;
import com.etime.service.StudentService;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
//@Component("ss") 之所以这里也可以用@Component的原因是因为该注解既可以用在dao层也可以用在service层
@Service("ss")
public class StudentServiceImpl implements StudentService {
@Override
public void show() {
System.out.println("这是studentServiceImpl里的方法");
}
}
用@Test进行测试上述代码
示例代码:
package com.etime.demo;
import com.etime.dao.StudentDao;
import com.etime.service.StudentService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentTest {
@Test
public void st01(){
//加载application.xml解析文件
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
//实例对象StudentDao
StudentDao studentDao =(StudentDao) context.getBean("sd");
studentDao.show();//调用studentDao实现方法
//实例对象StudentService
StudentService service=(StudentService) context.getBean("ss");
service.show();//调用studentService实现方法
}
}
运行结果:
@Value,@Resource,@Autowrited,@Qualiffer
注解 | 说明 |
---|---|
@Value | 注入普通属性 |
@Autowired | 自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型,当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功。找不到就报错。如果IOC容器(即反转控制)当中有多个接口得实现类,首先根据类型自动装配,然后再根据名称自动装配。 |
@Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |
@Resource | 相当于@Autowired+Qualifier,按照名称进行注入,是java提供的,不是框架提供的 |
使用@Value进行字符串的注入
示例代码:
@Value("字符串String Hello World")
private String string;
public void addStudent(){
System.out.println(string);
System.out.println("这样开始增加学生信息了");
}
使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入
方法一只使用@Autowired
package com.etime.service.Impl;
import com.etime.dao.StudentDao;
import com.etime.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
//@Component("ss") 之所以这里也可以用@Component的原因是因为该注解既可以用在dao层也可以用在service层
@Service("ss")
public class StudentServiceImpl implements StudentService {
@Autowired//只使用@Autowired注解把dao层的类注入
private StudentDao studentDao;
@Override
public void show() {
studentDao.show();
System.out.println("这是studentServiceImpl里的方法");
}
}
第二种方法
package com.etime.service.Impl;
import com.etime.dao.StudentDao;
import com.etime.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
//@Component("ss") 之所以这里也可以用@Component的原因是因为该注解既可以用在dao层也可以用在service层
@Service("ss")
public class StudentServiceImpl implements StudentService {
@Autowired//只使用@Autowired注解把dao层的类注入
@Qualifier("sd")//即使用@Qualifier又使用@Autowired注解将dao层的类注入
private StudentDao studentDao;
@Override
public void show() {
studentDao.show();
System.out.println("这是studentServiceImpl里的方法");
}
}
第三种方法
package com.etime.service.Impl;
import com.etime.dao.StudentDao;
import com.etime.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
//@Component("ss") 之所以这里也可以用@Component的原因是因为该注解既可以用在dao层也可以用在service层
@Service("ss")
public class StudentServiceImpl implements StudentService {
// @Autowired//只使用@Autowired注解把dao层的类注入
// @Qualifier("sd")//即使用@Qualifier又使用@Autowired注解将dao层的类注入
@Resource(name = "sd")//只使用当前@Resource一个注解能够解studentDao dao层的bean注入
private StudentDao studentDao;
@Override
public void show() {
studentDao.show();
System.out.println("这是studentServiceImpl里的方法");
}
}
测试:
示例代码:
@Test
public void st02(){
//加载application.xml解析文件
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
//dao层已经通过注解注入的方式代替了,不需要重新进行实例化
//实例对象StudentDao
// StudentDao studentDao =(StudentDao) context.getBean("sd");
// studentDao.show();//调用studentDao实现方法
//实例对象StudentService
StudentService service=(StudentService) context.getBean("ss");
service.show();//调用studentService实现方法
}
运行结果:
@PostConstruct @PreDestroy
注解 | 说明 |
---|---|
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
使用@PostConstruct标注初始化方法,使用@PreDestory标注销毁方法。注意,这两个注解是java提供的,不是spring提供的
示例代码:
package com.etime.dao.Impl;
import com.etime.dao.StudentDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
//@Component("sd")
@Repository("sd")//@Component和@Component都能够达到相同的效果,但是@Repository只能实例对象dao层
public class StudentDaoImpl implements StudentDao {
// @Value("字符串String Hello World")
// private String string;
// public void addStudent(){
// System.out.println(string);
// System.out.println("这样开始增加学生信息了");
// }
@PostConstruct
public void init(){
System.out.println("studentDao被初始化了");
}
@Override
public void show() {
System.out.println("这是studentDaoImpl的方法");
}
@PreDestroy
public void destroy(){
System.out.println("studentDao被摧毁了");
}
}
测试:
@Test
public void st03(){
//加载application.xml解析文件
ClassPathXmlApplicationContext context= new ClassPathXmlApplicationContext("application.xml");
StudentDao studentDao= (StudentDao) context.getBean("sd");//实例化studentDao类
studentDao.show();//调用studentDao里面的方法发
context.close();
}
运行结果:
Spring基于注解的IOC
建立如图的maven模块
注意:这里的依赖根据自己使用的数据库版本添加,我这里使用的是mysql8.0
示例代码:
pom.xml
<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.0modelVersion>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>6source>
<target>6target>
configuration>
plugin>
plugins>
build>
<properties>
<spring.version>5.2.5.RELEASEspring.version>
properties>
<groupId>com.etimegroupId>
<artifactId>sdartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.11version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
dependencies>
project>
application.xml
<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-3.0.xsd">
<context:component-scan base-package="com.etime">context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
beans>
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_school?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=h123456
示例代码:
dao:
package com.etime.dao;
import com.etime.entity.Student;
import java.util.List;
public interface StudentDao {
List<Student> getStudent();
}
daoImpl:
package com.etime.dao.Impl;
import com.etime.dao.StudentDao;
import com.etime.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
//@Component("sd")
@Repository("sd")//@Component和@Component都能够达到相同的效果,但是@Repository只能实例对象dao层
public class StudentDaoImpl implements StudentDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<Student> getStudent() {
List<Student> list=null;
String sql="select * from student";
list=jdbcTemplate.query(sql,new BeanPropertyRowMapper<Student>(Student.class));
return list;
}
}
service:
package com.etime.service;
import com.etime.entity.Student;
import java.util.List;
public interface StudentService {
List<Student> getAllStudent();
}
serviceImpl:
package com.etime.service.Impl;
import com.etime.dao.StudentDao;
import com.etime.entity.Student;
import com.etime.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
//@Component("ss") 之所以这里也可以用@Component的原因是因为该注解既可以用在dao层也可以用在service层
@Service("ss")
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
@Override
public List<Student> getAllStudent() {
return studentDao.getStudent();
}
}
测试:
示例代码 :
@Test
public void st04(){
//加载application.xml解析文件
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
StudentService service = (StudentService) context.getBean("ss");
List<Student> list= service.getAllStudent();
System.out.println(list);
}
运行结果:
通过上述案例可知上述的注解还不能够全部替换xml配置之文件,好需要进行注解替代配置
非定义的Bean配置:@Bean
加载proerties文件的配置:context:property-placeholder
组件扫描的配置:context:component-scan
引入其他文件的注解:@import
上述注解的说明:
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解,作用等价于application.xml配置文件 |
@ComponentScan | 用于指定Spring 在初始化容器时扫描包。作用和在Spring的xml配置文件中的 |
@Bean | 使用在方法上,标注将该方法的返回值存储到Spring容器中。id的值默认是方法的名称,可以自定义id的值 |
@PropertySource | 用于加载xxxx.properties 文件中的配置 结合@Value()取配置文件的值 |
@Import | 用于导入其他配置类 |
jdbc.properties
示例代码:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_school?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=h123456
在util工具包内创建DataSourceConfig.java类
代码:
package com.etime.util;
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 org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* 做数据库相关配置
* 读取properties文件内容
* 根据读取的内容生成连接池对象
* 根据连接池对象获取JdbcTemplate对象
* */
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String user;
@Value("${jdbc.password}")
private String password;
public DataSource getDataSource(){
ComboPooledDataSource ds= new ComboPooledDataSource();
try {
ds.setDriverClass(driverClass);
ds.setJdbcUrl(jdbcUrl);
ds.setUser(user);
ds.setPassword(password);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return ds;
}
@Bean(name="jdbcTemplate")
public JdbcTemplate getJdbcTemplate(){
return new JdbcTemplate(getDataSource());
}
}
同样创建SpringConfig.java类
代码:
package com.etime.util;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//指定当前类的配置类相当于application.xml
@Configuration
@Import(DataSourceConfig.class)//导入连接数据
@ComponentScan("com.etime")//扫描文件
public class SpringConfig {
}
上述就能够彻底替换了application.xml配置文件内的内容:
由第一种中方式下进行改造后将不再需要application.xml文件
修改后:
测试:
代码
@Test
public void st05(){
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
StudentService service = (StudentService) context.getBean("ss");
List<Student> list= service.getAllStudent();
System.out.println(list);
}
运行结果:
为了测试使用JUnit单元测试,但是在开发中将不适用该技术点时,不影响开发效果,但是可能影响开发效率。所以尽量避免代码重复性。
IOC测试类中的问题和解决
添加依赖(在pom.xml中)
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
使用注解完成容器的加载
@RunWith:替换原有运行器
@ContextConfiguration:指定Spring配置文件的位置
@Autowired:给测试类中的变量注入数据
示例代码:StudentTest.java
package com.etime.demo;
import com.etime.dao.StudentDao;
import com.etime.entity.Student;
import com.etime.service.StudentService;
import com.etime.util.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
//加载Spring的配置类,生成容器对象
@ContextConfiguration(classes = {SpringConfig.class})
public class StudentTest {
@Autowired
private ApplicationContext context;
@Test
public void st05(){
StudentService service = (StudentService) context.getBean("ss");
List<Student> list= service.getAllStudent();
System.out.println(list);
}
}
运行结果: