Spring学习记录之注解开发

前面我们已经了解了Spring是如何创建Bean以及完成依赖注入的,但我们会发现,其似乎并没有达到简化开发的目的,这是因为 我们还没有接触到注解开发。

注解开发之组件

我们在要使用Bean的地方使用@Comonpent(组件)注解,相当于在IOC容器中创建了标签,随后在里面写上名字代表bean的名字。

@Component("bookdao")
public class BookDaoImpl implements BookDao {public BookDaoImpl(){System.out.print("bookdao constructor running...\n");}public void save(){System.out.print("执行dao...\n");}public void init(){System.out.print("bookdao init...\n");}public void destory(){System.out.print("bookdao destory..\n");}
}

随后我们只需要在IOC容器中使用组件扫描包即可:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"
><context:component-scan base-package="dao.impl"/>
</beans>

在这里插入图片描述
此外,Spring为方便我们区分,认为都使用Component不方便,因此提供了三个衍生注解:Controller、Service以及Repository。他们三个的作用与Component完全相同,只是表示不同而已。

在这里插入图片描述
此时,我们看到在IOC容器中并没有太多的Bean内容了,只有一个扫描,那么,我们可不可以直接将XML去掉呢,那么就可以使用纯注解开发来实现这个功能:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration//代替整个Spring的那些头文件
@ComponentScan("dao.impl")//代替扫描组件操作
public class SpringConfig {
}

随后我们创建IOC容器并获取Bean,即由原本的读取配置文件变为了读取配置类。

import dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSrpingConfig {public static void main(String[] args) {ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);BookDao dataSource= (BookDao) ctx.getBean("bookdao");System.out.print(dataSource);}
}

事实上,配置类的形式仅仅是将配置文件换了一种表现方式罢了。
在这里插入图片描述

在这里插入图片描述

自动装配

前面已经提到,可以用settter或构造方法的形式来实现依赖注入,那么在注解开发中是否能够将这个过程再次简化,将setter方法也去除呢?Spring实现了这个功能:Autowrited,这个功能实际上是通过反射实现的。

反射原理

反射就是让对象认清自己:
在这里插入图片描述
而Java语言则具备反射的能力。
在这里插入图片描述

Java语言中反射的实现原理。Java的反射机制可以在运行时记录类的信息,包括类名、父类、接口、变量、方法等并将其存储在一个特殊的对象中,即Instance Class。通过Java Mirror,即Java镜像,可以访问这些信息,并以Java对象的形式表示。通过一系列的API,可以间接访问Instance Class中的信息。Java的反射机制是Java语言超越C++的关键因素之一,也是Java成为世界第一大编程语言的原因之一。

那么该如何实现呢?
首先是修改Spring配置文件,由于我们增添了Service层的对象,那么我们就需要扫描Service层。使用{}来扫描多个层。

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration//代表是配置文件
@ComponentScan({"dao.impl","service.impl"})//代表要扫的层
public class SpringConfig {
}

然后我们只需要在对应的ServiceImpl与OrderDaoImpl对象中添加注解即可。

package service.impl;
import dao.OrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import service.OrderService;
@Service("orderservice")//添加@Service注解,可以不加名字,此时加载Bean时只能通过类型加载,但这也是我们所推荐的,加了名字后就可以根据名字加载
public class OrderServiceImpl implements OrderService {@Autowired//必须使用该注解,可以代替原本的setter与构造方法的装配依赖方法。OrderDao orderdao;@Overridepublic void save() {orderdao.save();}
}

OrderDaoImpl的配置如下:

package dao.impl;
import dao.OrderDao;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository("orderdao")//加入持久层注解
public class OrderDaoImpl implements OrderDao {@Overridepublic void save() {System.out.print("orderdao save..");}
}

执行的话,我们使用名字来加载Bean,当然也可以按照类型。

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import service.OrderService;public class TestSrpingConfig {public static void main(String[] args) {ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);OrderService os= (OrderService) ctx.getBean("orderservice");os.save();}
}

需要提一下的是,@Autowired是通过类型进行装配的,因此如果在Service中需要加载两个相同类型的对象时,那么这时候如果只使用@Autowired则会报错。
比如我们现在创建一个OrderDaoImpl2,它也是OrderDao接口的实现类。

package dao.impl;
import dao.OrderDao;
import org.springframework.stereotype.Repository;
@Repository
public class OrderDaoImpl2 implements OrderDao {@Overridepublic void show() {}@Overridepublic void save() {System.out.print("orderdao2 save..");}
}

此时,需要注意的是,我们要将OrderDaoImpl前面的@Repository(“orderdao”)这的名字去掉,否则它还是会按照名字加载,在双方都没有名字后,我们再次运行会报错:
可以看到报错信息,找到了两个相同的类型,不知道应该加载哪一个。

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'dao.OrderDao' available: expected single matching bean but found 2: orderDaoImpl,orderDaoImpl2

此时我们将其修改一下,加上名字,这里通过@Qualifier这个注解来指定注入哪个Bean,同时我们也发现如果不加@Qualifier的话,会按照起的变量名来匹配,如下面,如果不加@Qualifier,则会找到orderdao2,但加了之后,便会加载orderdao,因此可以看到要想指定同类型的Bean进行注入,我们还是要使用@Qualifier这个注解,同时也发现,如果把@Qualifier去掉,然后起名字叫orderdao1的话,就会再次报之前的错误了。

package service.impl;
import dao.OrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import service.OrderService;
@Service("orderservice")
public class OrderServiceImpl implements OrderService {@Autowired@Qualifier("orderdao")OrderDao orderdao2;@Overridepublic void save() {orderdao2.save();}
}

需要注意的是,Spring是通过暴力反射的方式实现了注解开发,这事实上与IOC思想有些冲突,因此IOC的思想是创建Bean在外部,你给我提供对应的接口(setter方法)即可,而通过这种注解开发的方式,我们可以去除setter方法。
同时,注解开发是通过无参构造方法的形式实现的,因此我们必须保证它有无参构造方法,即我们只要不重写构造方法就可以了。
上面是实现引用类型注入,那么如何实现简单类型注入呢?很简单,使用Value注解即可。

在这里插入图片描述
那么这样使用Value注解来实现简单类型注入有啥问题呢,从上面的代码可以看出,我们的值是写在里面的,这样的话和我们直接在生成变量时便给赋值效果是相同的,那么这样写的意义在哪,事实上,这是为了方便我们从外部文件(如properties)中获取数据。

外部properties文件简单类型注入

那么我们该如何实现呢?
首先,我们要有一个先创建一个db.properties文件:

jdbc.url = jdbc\:mysql\:///ssm_takeout?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
jdbc.driver = com.mysql.jdbc.Driver
jdbc.username= root
jdbc.password= root
name=libai

随后我们需要让我们的Spring配置 文件找到这个properties文件:

@Configuration
@ComponentScan({"dao.impl","service.impl"})
@PropertySource("db.properties")//注意,这里是不能使用通配符的。
public class SpringConfig {
}

然后我们将需要获得的参数与properties文件中的属性对应一下:

@Service("orderservice")
public class OrderServiceImpl implements OrderService {@Autowired@Qualifier("orderdao")OrderDao orderdao;@Value("${name}")//这里对应name名字private String name;@Overridepublic void save() {System.out.print(name+"\n");orderdao.save();}
}

运行结果:

在这里插入图片描述

管理第三方Bean

我们重新定义一个Spring配置类,并使用@Bean注解规定返回结果是一个Bean

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.beans.PropertyVetoException;
@Configuration
public class SpringConfigOtherBean {//1.定义一个方法来获取要管理的对象//2.添加@Bean注解,表示当前方法返回的是一个Bean@Beanpublic ComboPooledDataSource getDataSource() throws PropertyVetoException {ComboPooledDataSource ds = new ComboPooledDataSource();ds.setDriverClass("com.mysql.jdbc.Driver");ds.setUser("root");ds.setJdbcUrl("jdbc\\:mysql\\:///ssm_takeout");ds.setPassword("root");return ds;}
}

随后,我们运行一下:

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSrpingConfig {public static void main(String[] args) {ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfigOtherBean.class);ComboPooledDataSource ds=ctx.getBean(ComboPooledDataSource.class);System.out.print(ds);}
}

但需要注意的是,我们 先前是将数据库的配置也写到Spring的配置中了,如果我们把所有的文件都放到Spring配置中的话,这个代码的可读性就会很差,因此我们需要将这些文件独立出来。
怎么写呢,很简单,使用Import注解即可:
原始的Spring配置类如下:

package configs;import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;@Configuration
@Import(JdbcConfig.class)//导入其他配置,若有多个,则使用{}
public class SpringConfig {
}

JDBC的配置类:

package configs;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import java.beans.PropertyVetoException;
public class JdbcConfig {//1.定义一个方法来获取要管理的对象//2.添加@Bean注解,表示当前方法返回的是一个Bean@Beanpublic ComboPooledDataSource getDataSource() throws PropertyVetoException {ComboPooledDataSource ds = new ComboPooledDataSource();ds.setDriverClass("com.mysql.jdbc.Driver");ds.setUser("root");ds.setJdbcUrl("jdbc\\:mysql\\:///ssm_takeout");ds.setPassword("root");return ds;}
}

再次运行,正常。

第三方Bean依赖注入

前面在配置第三方Bean管理时,发现我们的数据库配置信息直接写在了里面,这是不允许的,我们需要修改一下:
我们首先需要加载一下db.properties文件

package configs;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:db.properties")
public class SpringConfig {
}

随后修改一下JDBCConfig中的配置:

package configs;import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import java.beans.PropertyVetoException;
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;//1.定义一个方法来获取要管理的对象//2.添加@Bean注解,表示当前方法返回的是一个Bean@Beanpublic ComboPooledDataSource getDataSource() throws PropertyVetoException {ComboPooledDataSource ds = new ComboPooledDataSource();ds.setDriverClass(driver);ds.setUser(username);ds.setJdbcUrl(url);ds.setPassword(password);return ds;}
}

这是基本数据类型在第三方Bean中的注入方式,那么如果是其他的Bean类型呢,其实Spring的设计更为简单。
首先需要在Spring配置中扫描我们要引用的Bean所在的包:

package configs;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:db.properties")
@ComponentScan("dao")
public class SpringConfig {
}

随后我们便可以直接在第三方Bean所对应的方法中获取了,直接以形参的方式便可以实现了,这是由于我们的自动装配。

package configs;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import dao.OrderDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import java.beans.PropertyVetoException;
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;//1.定义一个方法来获取要管理的对象//2.添加@Bean注解,表示当前方法返回的是一个Bean@Beanpublic ComboPooledDataSource getDataSource(OrderDao orderdao) throws PropertyVetoException {ComboPooledDataSource ds = new ComboPooledDataSource();System.out.print(orderdao);ds.setDriverClass(driver);ds.setUser(username);ds.setJdbcUrl(url);ds.setPassword(password);return ds;}
}

总结一下,在第三方Bean的管理中,我们需要使用@Bean注解来获取所需要的Bean,对于第三方Bean中需要获取的数据,引用类型使用方法形参,基本类型使用成员变量。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/557381.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 文件系统:重定向、缓冲区

目录 一、重定向 1、输出重定向 2、输入重定向 3、追加重定向 4、dup2 系统调用 二、理性理解Linux系统下“一切皆文件” 了解硬件接口 三、缓冲区 1、为什么要有缓冲区? 2、刷新策略 3、缓冲模式改变导致发生写时拷贝 未创建子进程时 创建子进程时 使用fflush…

某招聘系统0day挖掘(获取4站点报告证书)

前言: 21年的挖的漏洞了 漏洞均已提交且均已修复,这里文章只做技术交流 挖掘过程 对我来说,毕竟喜欢直接黑盒挖0day,一个0day挖到后就可以刷上百分。 如该系统正常找了一个招聘系统用的比较多的 如该通用系统,该通用系统存在一个注册功能 正常的进行注册一个账户进去…

算法打卡day11

今日任务&#xff1a; 1&#xff09;239. 滑动窗口最大值 2&#xff09;347.前 K 个高频元素 239. 滑动窗口最大值 题目链接&#xff1a;239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移…

Windows 11 鼠标右键可选择 cmd 命令行选项

** Windows 11 鼠标右键可选择 cmd 命令行选项 ** 在文件夹内打开命令行&#xff0c;只能使用 Windows 自带的 PowerShell &#xff0c; 作为一个 cmd 重度使用用户来说很是折磨&#xff0c;需要打开 cmd 然后切换盘符再 cd 。。。 现在咱们自己创建一个可以打开 cmd 的方法…

【Leetcode每日一题】 递归 - Pow(x, n)(难度⭐⭐)(40)

1. 题目解析 题目链接&#xff1a;50. Pow(x, n) 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 在这个算法中&#xff0c;递归函数的任务是求出 x 的 n 次方。那么&#xff0c;这个函数是怎么工作的呢&#xff1f;它…

关于Devin,它只是人类历史的一个正常延续而已

近期&#xff0c;关于Devin的讨论无疑成为了科技界的热点话题。作为一款由初创公司Cognition AI研发的AI程序员&#xff0c;Devin凭借其惊人的“端到端解决成功率”和高效完成需求的能力&#xff0c;在各大社交媒体和科技论坛上引发了广泛的讨论。然而&#xff0c;在这场看似前…

LeetCode刷题记录:(13)N皇后(难题不难)

leetcode传送通道 传说中的N皇后&#xff0c;不难&#xff0c;进来了就看完吧 注释序号代表鄙人写代码的顺序和思考逻辑&#xff0c;供参考 class Solution {// 1.定义结果数组List<List<String>> result new ArrayList<>();public List<List<String&…

Learn OpenGL 23 阴影映射

阴影 阴影是光线被阻挡的结果&#xff1b;当一个光源的光线由于其他物体的阻挡不能够达到一个物体的表面的时候&#xff0c;那么这个物体就在阴影中了。阴影能够使场景看起来真实得多&#xff0c;并且可以让观察者获得物体之间的空间位置关系。场景和物体的深度感因此能够得到…

你要的个性化生信分析服务今天正式开启啦!定制你的专属解决方案!全程1v1答疑!

之前在 干货满满 | 给生信小白的入门小建议 | 掏心掏肺版 中有提到&#xff0c;如果小伙伴们真的想学好生信&#xff0c;那编程能力是必须要有的&#xff01;但是可能有些小伙伴们并没有那么多的时间从头开始学习编程&#xff0c;又或是希望有人指导或者协助完成生信分析工作&a…

32.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-网络数据分析原理与依据

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;31.其它消息的实…

python中获取当前项目的目录

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 今天介绍一下&#xff0c;如何在python中获取当前项目所在的目录&#xff0c;而不是运行脚本的目录。 class ProjectPaths:# 初始化时获取当前脚本的路径staticmethoddef get_script_dir():…

【每日八股】Java基础经典面试题4

前言&#xff1a;哈喽大家好&#xff0c;我是黑洞晓威&#xff0c;25届毕业生&#xff0c;正在为即将到来的秋招做准备。本篇将记录学习过程中经常出现的知识点以及自己学习薄弱的地方进行总结&#x1f970;。 本篇文章记录的Java基础面试题&#xff0c;如果你也在复习的话不妨…