Spring的注解开发是由2.0版开始支持注解,2.5版注解功能趋于完善,3.0版支持纯注解开发。Spring的注解开发便可以真正意义上简化开发
一些常用注解
1.@Component:设置该类为spring管理的bean
- @Controller:表现层
- @Service:业务逻辑层
- @Repository:数据访问层
2.@ComponentScan:设置spring配置类扫描路径,用于加载使用注解格式定义的bean
3.@Import:导入配置类
4.@Autowired:为引用类型属性设置值
5.@Qualifier:为引用类型属性指定注入的beanId
6.@Value:为基本数据类型或字符串类型属性设置值
7.@PropertySource:加载properties文件中的属性值
8.@Bean:设置该方法的返回值作为spring管理的bean
9.@Scope:设置该类创建对象的作用范围,建出的bean是否为单例对象
10.@PostConstruct:设置该方法为初始化方法
11.@PreDestroy:设置该方法为销毁方法
一、注解开发定义bean
创建一个新Maven模块
在pom.xml中添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
在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"
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 https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
在java包下创建BookDao、BookDaoImpl、BookService、BookServiceImpl类
BookDao
public interface BookDao {
public void save();
}
BookDaoImpl
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ..." );
}
}
BookService
public interface BookService {
public void save();
}
BookServiceImpl
public class BookServiceImpl implements BookService {
@Override
public void save() {
System.out.println("book service save ...");
}
}
在Spring配置文件开发中是使用bean标签来创建bean对象
<bean id="bookDao" class="com.dao.impl.BookDaoImpl"/>
在注解开发中,可以直接使用@Component来代替bean标签,在BookImpl类上添加@Component注解
@Component相当于bean标签,括号中的值为bean对象id,在那个实现类上添加的注解即为bean标签的class属性
@Component("bookDao")
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ..." );
}
}
因为接口无法创建对象,所以@ComPonent不可以添加在接口上
在applicationContext.xml利用context:componenr-scan标签扫描注解包
Component-scan在扫描包路径时,包路径越多(如:com.dao.impl),扫描范围越小,速度越快;包路径越少(如:com),扫描范围越大,速度越慢
<context:component-scan base-package="com"/>
在java包下新建一个Test测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
控制台输出
@ComPonent注解也可直接添加到类上,不用写id,使用时按类型获取bean
在BookServiceImpl类上加入@Component注解
@Component
public class BookServiceImpl implements BookService {
@Override
public void save() {
System.out.println("book service save ...");
}
}
在测试类中加入获取BookServiceImp类型所对应的bean
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
BookService bookService = ctx.getBean(BookService.class);
bookService.save();
}
}
控制台输出
@Component注解如果不起名称,会有一个默认值就是当前类名首字母小写(bookServiceImpl),所以也可以按照名称获取
BookService bookService = (BookService)ctx.getBean("bookServiceImpl");
bookService.save();
控制台输出同上
对于@Component注解,其衍生出了其他三个注解@Controller、@Service、@Repository,这三个注解和@Component注解作用相同,其目的是为了在编写时能更好的区分这个类是属于表现层、业务层还是数据层
- @Controller:表现层
- @Service:业务逻辑层
- @Repository:数据访问层
二、纯注解开发
在Spring注解开发中任用到了配置文件,在配置文件中对包进行了扫描
在Spring3.0中开启的纯注解开发模式,便利用java类替代了配置文件,开启了Spring快速开发赛道
在com包下创建一个配置类config.Springconfig
@Configuration注解可标识一个配置类,替换applicationContext.xml
@ComponentScan注解替换<context:component-scan base-package=""/>
@Configuration
@ComponentScan("com")
public class Springconfig {
}
@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据使用数组格式
@ComponentScan({"com.service","com.dao"})
将applicationContext.xml配置文件删除
创建一个新的Test02测试类
注意加载配置文件初始化容器由ClassPathXmlApplicationContext("applicationContext.xml")变更为AnnotationConfigApplicationContext(SpringConfig.class)
public class Test02 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Springconfig.class);
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
BookService bookService = ctx.getBean(BookService.class);
bookService.save();
}
}
控制台输出
三、bean的作用范围
bean的scope属性也可通过@Scope注解实现
在BookDaoImpl类上添加@Scope注解
singleton:单例(默认)prototype:非单例
@Repository
@Scope("prototype")
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ..." );
}
}
创建一个新的Test03测试类
public class Test03 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Springconfig.class);
BookDao bookDao1 = (BookDao) ctx.getBean("bookDaoImpl");
BookDao bookDao2 = (BookDao) ctx.getBean("bookDaoImpl");
System.out.println(bookDao1);
System.out.println(bookDao2);
}
}
控制台输出
四、bean的生命周期
在Spring注解开发中,可以用@PostConstruct和@PreDestroy注解替换init-method属性和destroy-method属性
在BookDaoImpl中添加两个方法,init和destroy ,方法名任意
在对应方法上添加@PostConstruct和@PreDestroy注解
@PostConstruct :在构造方法之后执行
@PreDestroy :在销毁方法之前执行
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ..." );
}
@PostConstruct
public void init(){
System.out.println("init ...");
}
@PreDestroy
public void destory(){
System.out.println("destory ...");
}
}
创建一个Test04测试类
public class Test04 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Springconfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.save();
ctx.close();
}
}
控制台输出
如果@PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
JDK9以后jdk中的javax.annotation包被移除了,这两个注解刚好就在这个包中
五、注解开发依赖注入
1.引用类型注入
Spring为了使用注解简化开发,并没有提供构造函数注入、setter注入对应的注解,只提供了自动装配的注解实现,@Autowired
在BookServiceImpl定义BookDao属性,并添加@Autowired注解
@Autowired可以写在属性上,也可以写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
// @Autowired
// public void setBookDao(BookDao bookDao) {
// this.bookDao = bookDao;
// }
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
为什么setter方法可以删除?
- 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
- 普通反射只能获取public修饰的内容
- 暴力反射除了获取public修饰的内容,还可以获取private修饰的内容
- 所以无需提供set方法
创建一个Test05测试类
public class Test05 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Springconfig.class);
BookService bookService = ctx.getBean(BookService.class);
bookService.save();
}
}
控制台输出
没有destroy是因为没有关闭容器或注册钩子
@Autowired是按照类型注入,那么对应的BookDao接口如果有多个实现类,Test05将会报错
此时,按类型注入就无法区分到底注入那个对象,可通过按名称注入解决
新建一个BookDaoImpl2
@Repository
public class BookDaoImpl2 implements BookDao {
@Override
public void save() {
System.out.println("book dao save ... 2" );
}
}
此时运行Test05会报错找不到对应bean
给BookDaoImpl和BookDaoImpl2分别起个名称
BookDaoImpl
@Repository("bookDao")
BookDaoImpl2
@Repository("bookDao2")
此时再运行Test05将正常运行并找到名称为bookDao的bean对象
@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称去匹配。只要找到一个变量名叫bookDao且容器中只有一个叫bookDao的bean,就可以成功注入
如果容器中同一类型有多个bean,注入参数的属性名又和容器中bean的名称不一致,此时可以同通过@Qualifier注解来指定注入那个名称的bean对象
将BookDaoImpl的名称改为BookDao1
@Repository("bookDao1")
此时Test05报错NoUniqueBeanDefinitionException
在BookServiceImpl中添加@Qualifier注解
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
// @Autowired
// public void setBookDao(BookDao bookDao) {
// this.bookDao = bookDao;
// }
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
@Qualifier注解不能独立使用,必须和@Autowired一起使用
运行Test05,控制台输出
2.简单类型注入
以上自动装配皆是针对引用类型注入,对与简单类型注入,可直接使用@Value注解
在BookDaoImpl中加入一个私有成员属性
@Repository("bookDao1")
public class BookDaoImpl implements BookDao {
@Value("wanger")
private String name;
@Override
public void save() {
System.out.println("book dao save ..." + name );
}
@PostConstruct
public void init(){
System.out.println("init ...");
}
@PreDestroy
public void destory(){
System.out.println("destory ...");
}
}
@Value注解中数据类型格式要匹配,如将“wanger”注入给int值程序将报错
控制台输出
这样看@Value注解和直接赋值是一个效果,还没有直接赋值简单,所以@Value注解一般会被用在从properties配置文件中读取内容进行使用
六、注解读取properties配置文件
在resource下准备properties文件
jdbc.properties
name = wangerhhh
在SpringConfig中加入@PropertySource注解
@Configuration
@ComponentScan("com")
@PropertySource("jdbc.properties")
public class Springconfig {
}
如果读取的properties配置文件有多个,使用数组来指定
@PropertySource({"jdbc.properties","xxx.properties"})
@PropertySource注解属性中不支持使用通配符* ,运行会报错
@PropertySource({"*.properties"})
@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件
@PropertySource({"classpath:jdbc.properties"})
再使用@Value再BookDaoImpl下读取配置文件中内容
@Repository("bookDao1")
public class BookDaoImpl implements BookDao {
@Value("${name}")
private String name;
@Override
public void save() {
System.out.println("book dao save ..." + name);
}
@PostConstruct
public void init() {
System.out.println("init ...");
}
@PreDestroy
public void destory() {
System.out.println("destory ...");
}
}
运行Test05,控制台输出
七、注解开发管理第三方bean
当我们管理第三方bean对象时,可用@Bean注解实现
新建一个Maven模块
在pom.xml中添加相关依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
在Java包下添加BookDao、BookDaoImpl类
BookDao
public interface BookDao {
public void save();
}
BookDaoImpl
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ..." );
}
}
添加配置类SpringConfig,JdbcConfig配置类
SpringConfig
@Configuration
@ComponentScan("com.config")
public class SpringConfig {
}
JdbcConfig
dataSource方法也可以写在SpringConfig类中,但是不利于代码阅读和分类
@Configuration
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql:///library?useSSL=false");
ds.setUsername("root");
ds.setPassword("1234");
return ds;
}
}
创建一个Test测试类
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);
}
}
控制台输出
除了使用@ComponentScan包扫描注解,还可以使用@Import手动引入注解
在@Import注解时,JdbcConfig配置类不用加@Configuration
//@Configuration
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql:///library?useSSL=false");
ds.setUsername("root");
ds.setPassword("1234");
return ds;
}
}
在SpringConfig中
@Import参数需要的是一个数组,可以用数组的方式引入多个配置类,但@Import注解在配置类中只能写一次,不允许重复定义
@Configuration
//@ComponentScan("com.config")
@Import(JdbcConfig.class)
public class SpringConfig {
}
控制台输出结果同上
八、注解开发实现为第三方bean注入资源
1.简单数据类型注入
当我们写数据库四要素时,一般写在properties文件中
在resource下准备一个jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///library?useSSL=false
jdbc.username=root
jdbc.password=1234
在JdbcConfig类下加入四要素对应的成员变量
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;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
System.out.println(url);
return ds;
}
}
在SpringConfig下使用@PropertySource导入JdbcConfig.class
@Configuration
@Import(JdbcConfig.class)
@PropertySource("jdbc.properties")
public class SpringConfig {
}
运行Test测试类
2.引用数据类型注入
当我们在构建DataSource对象的时候,需要用到BookDao对象可通过包扫描,将BookDao设置为DataSource的形参即可实现
将BookDaoImpl添加@Repository注解
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ...");
}
}
在SpringConfig中添加@ComponentScan("com.dao")扫描dao包
扫描的目的是让 Spring 能管理到 BookDao, 要让 IOC 容器中有一个 bookDao 对象
@Configuration
//@ComponentScan("com.config")
@ComponentScan("com.dao")
@Import(JdbcConfig.class)
@PropertySource("jdbc.properties")
public class SpringConfig {
}
直接在dataSource方法中注入形参bookDao
引用类型注入只需要为bean定义方法设置形参,容器会根据类型自动装配对象
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
System.out.println(url);
return ds;
}
运行Test,控制台输出