Spring IOC/DI


Spring


Spring 核心功能:IOC和AOP

IOC: 控制反转AOP: 面向切面编程

Spring Boot 中使用日志


Spring Boot 提供了日志支持,使用非常方便

  • 控制台输出语言 System.out.println() 是IO操作,性能很差,使用过多会严重影响系统性能

  • 日志系统提供了高性能缓存机制,有着很好的性能,并且可以使用配置进行统一打开和关闭

  • Spring Boot 整合了日志系统,简单配置就可以使用。

使用日志:

  1. 在需要日志输出的类中创建日志对象 loggerprivate static Logger logger = LoggerFactory.getLogger(LoggerTests.class);

  • 其中 使用static好处是方便在静态方法调用

  • Logger和LoggerFactory 来自org.slf4j包

  • 方法参数是当前的类名,用于设置Logger的层级,方便分组管理,分组打开和关闭

  1. 需要输出信息时候,使用logger的日志方法输出信息

  • 如果一般信息就使用 debug()

  • 如果是严重故障就使用 error()

  • 使用{}占位符输出变量信息

  1. 在application.properties 文件中设置日志门槛logging.level.cn.tedu.springioc = debug

  • 默认的日志级别是info

  • cn.tedu.springioc 是开启日志的包或者类,可以粗粒度设置到包,或者细粒度的类

  • 门槛级别用于控制日志的输出

  • 当前的设置为: cn.tedu.springioc包和子包中的日志输出语句高于debug级别的信息会被输出。

@SpringBootTest

publicclassLoggerTests {

privatestaticLoggerlogger=LoggerFactory.getLogger(LoggerTests.class);

@Test

voiddebug() {

logger.debug("Hello World!");

}

@Test

voidinfo() {

logger.info("普通信息");

}

@Test

voidwarn() {

logger.warn("警告信息");

}

@Test

voiderror() {

logger.error("错误信息");

}

@Test

voidvalue() {

logger.debug("今天时间 {}, {}", LocalDateTime.now(), "OK");

}

}

logging.level.cn.tedu.springioc = debug

常见的日志等级划分方式如下:

FATAL:致命等级的日志,指发生了严重的会导致应用程序退出的事件。ERROR:错误等级的日志,指发生了错误,但是不影响系统运行。WARN: 警告等级的日志,指发生了异常,可能是潜在的错误。INFO: 信息等级的日志,指一些在粗粒度级别上需要强调的应用程序运行信息。DEBUG:调试等级的日志,指一些细粒度的对于程序调试有帮助的信息。TRACE:跟踪等级的日志,指一些包含程序运行详细过程的信息。

使用组件扫描隐式创建对象


隐式创建对象:

  1. 在SpringBoot包的子包中类上标注@Compoment

  1. 使用时候使用@Autowired注入组件

  1. Spring Boot在启动时候会自动创建Bean组件, 这种方式被称为隐式创建对象

复习内容

@Component

publicclassDemoBean {

@Override

publicStringtoString() {

return"demoBean";

}

}

@SpringBootTest

publicclassComponentTests {

Loggerlogger=LoggerFactory.getLogger(ComponentTests.class);

@Autowired

DemoBeandemoBean;

@Test

voiddemoBean(){

logger.debug("{}", demoBean);

}

}

@Bean注解显示定义Bean


如果需要使用第三方是组件就不能在源码上标注@Component,也就不能使用组件扫描功能了。Spring提供了@Bean注解,在Spring 配置类中声明Java Bean。这种方式被称为"显示定义Bean"

使用方式:

  1. 在组件扫描的子包中创建一个配置类, 配置类上标注@Configuration

  1. 定义创建组件的方法, 在方法上标注@Bean

  1. Spring Boot 在启动时候,会自动扫描到配置类,创建Java Bean

  1. 声明Bean对象的方法名就是组件的默认ID,可以用于解决歧义问题

  1. 可以设置@Bean的属性设置BeanID

@Configuration

publicclassConfigBeans {

/**

* 使用@Bean 显示声明Java Bean 组件

* Bean ID 为 names

* @return 创建的JavaBean

*/

@Bean

publicArrayList<String>names(){

ArrayList<String>names=newArrayList<>();

names.add("Tom");

names.add("Jerry");

returnnames;

}

@Bean

publicArrayList<String>mobilePhone(){

ArrayList<String>list=newArrayList<>();

list.add("110");

list.add("119");

returnlist;

}

/**

* 使用 @Bean的属性设置BeanID

* @return 城市列表

*/

@Bean("cities")

publicArrayList<String>list(){

ArrayList<String>list=newArrayList<>();

list.add("北京");

list.add("上海");

returnlist;

}

}

@SpringBootTest

publicclassBeanTests {

Loggerlogger=LoggerFactory.getLogger(BeanTests.class);

@Autowired

@Qualifier("cities")

ArrayList<String>names;

@Test

voidnames() {

logger.debug("{}", names);

}

}

使用 ApplicationContext 获取Bean


ApplicationContext 直接翻译为应用程序上下文:

  • 是Spring的核心,简单理解ApplicationContext就是Spring容器

  • 所有IOC管理的JavaBean组件都在ApplicationContext对象中

  • 可以通过ApplicationContext的API方法获取任何被Spring管理的Bean对象

  • 有两种获取 ApplicationContext 方式

  • SpringApplication.run(ConfigBeans.class);

  • 注入 ApplicationContext

案例:

publicclassDemo01 {

publicstaticvoidmain(String[] args) {

ApplicationContextcontext=

SpringApplication.run(ConfigBeans.class);

List<String>list =context.getBean("names",

ArrayList.class);

System.out.println(list);

}

}

@SpringBootTest

publicclassApplicationContextTests {

@Autowired

ApplicationContextcontext;

@Test

voidcontext(){

List<String>list=context.getBean("names", List.class);

System.out.println(list);

}

}

Spring Boot 自动组件扫描


Spring Boot 会自动开启组件扫描功能:

  • 扫描启动类的当前包或者子包中的注解,并且创建对象

  • @ComponentScan Spring Boot 会自动配置 这个组件扫描注解

  • 如果 Java Bean 没有定义在当前包和子包中则扫描不到

  • 如果 Bean 创建到其他位置(不是当前包和子包)则需要手动配置组件扫描

  • 如果在启动类上配置了组件扫描,则会关闭自动组件扫描

注意: @Component 注解需要和 @ComponentScan 注解一起使用才可以

packagecn.test.bean;

importorg.springframework.stereotype.Component;

@Component

publicclassExampleBean {

@Override

publicStringtoString() {

return"exampleBean";

}

}

@ComponentScan({"cn.tedu.service","cn.test.bean"})

@SpringBootTest

publicclassExampleBeanTests {

Loggerlogger=LoggerFactory.getLogger(ExampleBeanTests.class);

@Autowired

ExampleBeanexampleBean;

@Test

voidexampleBean(){

logger.debug("{}", exampleBean);

}

}

关于组件扫描范围

组件扫描范围不能过大,如果扫描范围过大启动时候会变慢,严重情况下会慢几秒。最佳的方式是减少扫描范围,提升扫描效率:

  • 很糟:@ComponentScan({"org", "com"})

  • 仍然很糟: @ComponentScan("org")

  • 还行:@ComponentScan("com.bank.app")

  • 不错:@ComponentScan({"com.bank.app.repository", "com.bank.app.service")})

扫描多个包使用静态数组作为参数:{"com.bank.app.repository", "com.bank.app.service")}

@Bean 和 @Component 用途区别

@Bean显示配置Bean

  • 优点:

  • 集中在一个(或几个)地方,方便统一管理Bean

  • 编写任何你需要的Java代码,可以在配置类中自定义初始化程序

  • 可以对配置类进行单元测试,可以单独测试

  • 可用于所有类(不只是你自己的类)

  • 缺点:

  • 比@Component注解更加冗余,繁琐

@Component 隐式配置Bean,对你自己的Bean类非常友好,适合自己定义的Java类

  • 优点:

  • 编辑位置单一(就在类中)

  • 适合非常快速的开发,直接标注在源码上,很方便

  • 缺点:

  • 配置分布在你的代码库中

  • 不方便,难以调试/维护

  • 只适用于你自己的代码

建议显式配置与隐式配置的混合使用,可以通过多种方式进行混合

  • 使用隐式@Component配置

  • 你自己的类:配置@Component简单方便

  • @Bean显式配置用于:

  • 别人写好的类,添加注解的第三方Bean,使用@Bean方式配置

  • 不能修改的遗留代码

  • 当在单一位置管理配置的时候

Bean的作用域


Bean的作用域就是指Spring中Java Bean 有效范围,

  • 默认的作用域是单例的(singleton):任何时候获得的Bean对象都是同一个实例

  • 原型作用域“prototype”:每次引到bean时都会创建新的实例

其他作用域(了解):

作用域

描述

singleton

持续时间与 ApplicationContext 一致

prototype

每次调用 getBean() 都会返回一个新的对象 持续时间与持有引用的时间一致,没有引用后,会被作为垃圾而回收

session

持续时间与用户的HTTP会话一致 (Web 环境)

request

持续时间与用户的HTTP请求一致 (Web 环境)

application

持续时间与ServletContext一致(Spring 4.0)

global

持续时间与Portlet应用程序中的全局HttpSession一致(Spring 5开始过期

thread

持续时间与所在的线程一致,在Spring中已定义,但默认未注册

websocket

持续时间与websocket一致(Spring 4.2)

refresh

可以超过其application context的重新加载时间 难以确保效果,假设Spring Cloud**配置服务器

组件扫描案例:

@Component

@Scope("singleton")

publicclassSingletonBean {

@Override

publicStringtoString() {

return"SingletonBean";

}

}

@Component

@Scope("prototype")

publicclassPrototypeBean {

@Override

publicStringtoString() {

return"PrototypeBean";

}

}

@Autowired

ApplicationContextcontext;

@Test

voidsingleton(){

SingletonBeanbean1=context.getBean(SingletonBean.class);

SingletonBeanbean2=context.getBean(SingletonBean.class);

SingletonBeanbean3=context.getBean(SingletonBean.class);

logger.debug("{}", bean1);

logger.debug("bean1 和 bean2 是否是同一个对象{}", bean1==bean2);

logger.debug("bean1 和 bean3 是否是同一个对象{}", bean1==bean3);

}

@Test

voidprototype(){

PrototypeBeanbean1=context.getBean(PrototypeBean.class);

PrototypeBeanbean2=context.getBean(PrototypeBean.class);

PrototypeBeanbean3=context.getBean(PrototypeBean.class);

logger.debug("{}", bean1);

logger.debug("bean1 和 bean2 是否是同一个对象{}", bean1==bean2);

logger.debug("bean1 和 bean3 是否是同一个对象{}", bean1==bean3);

}

显示声明@Bean的案例:

@Configuration

publicclassScopeConfig {

@Bean

publicList<String>provinceNames() {

List<String>provinceNames=newArrayList<>();

provinceNames.add("北京");

provinceNames.add("上海");

returnprovinceNames;

}

@Bean

@Scope("prototype")

publicList<String>holidayNames() {

List<String>list=newArrayList<>();

list.add("中秋");

list.add("国庆");

returnlist;

}

}

@Test

voidbeanScope(){

List<String>provinceNames1=context.getBean("provinceNames", List.class);

List<String>provinceNames2=context.getBean("provinceNames", List.class);

logger.debug("{}", provinceNames1);

logger.debug("省份列表是否为单例:{}", (provinceNames2==provinceNames1));

List<String>holidayNames1=context.getBean("holidayNames", List.class);

List<String>holidayNames2=context.getBean("holidayNames", List.class);

logger.debug("{}", holidayNames1);

logger.debug("省份列表是否为单例:{}", (holidayNames1==holidayNames2));

}

@Autowired注入 和 @Resource注入


@Autowired注入

  • @Autowired注解是Spring框架提供的注解

  • @Autowired默认是根据类型来匹配的

  • @Autowired注入方式-3种

  • 构造方法注入(推荐的做法),必须存在唯一的匹配类型的依赖,只有一个构造器时候可以省略

  • set方法注入(不是Set方法也可以),构造方法注入和setter注入均支持多参数注入

  • 字段注入(对象属性注入),即便是private字段也可以注入,Spring 5 会有警告

  • @Autowired依赖:默认是必须!

  • 使用required属性覆盖默认行为@Autowired(required=false)

@Autowired注入歧义问题

若注入的类型对应的实例存在多个,则编译报错/运行时报错,解决办法有2种:

  • 注入对象的名称对应某个bean ID,则可以注入成功

@Autowired的注入机制:

  • 先根据类型匹配

  • 若没有匹配类型,报错

  • 有匹配类型,对应的实例有1个,则注入

  • 有匹配类型,对应的实例有多个,则自动根据name匹配

  • 根据name匹配(就是根据BeanID匹配)

  • 如果name匹配成功就注入,如果根据名称匹配失败则报错

可以添加@Qualifer注解指定beanID

@Component

publicclassAxeimplementsTool{

@Override

publicStringtoString() {

return"开天斧";

}

}

@Component

publicclassSawimplementsTool{

@Override

publicStringtoString() {

return"寒冰锯";

}

}

@Component

publicclassWorker {

Loggerlogger=LoggerFactory.getLogger(Worker.class);

privateStringname="光头强";

@Autowired

@Qualifier("saw")

privateTooltool;

publicWorker() {

}

publicvoidwork(){

logger.debug("{}使用{}砍树", name, tool);

}

}

@SpringBootTest

publicclassDITests {

@Autowired

Workerworker;

@Test

voidworker() {

worker.work();

}

}

关于@Resource

来自JSR-250,能被EJB 3.0和Spring支持

  • 默认根据名称,而不是默认按类型来识别依赖项,名称是Spring的Bean名称

  • 根据属性/字段的名称进行推断

  • 若找不到匹配的name,则直接回退到根据类型注入

  • @Autowired是先根据类型来匹配的

  • 仅支持Setter和字段注入

简单理解:@Autowired 和 @Resource区别

  • 注入匹配规则不同:

  • @Autowired 先匹配类型, 在匹配名称, @Resource先匹配名称, 再匹配类型。

  • 支持的注入方式不同:

  • @Autowired: 属性 方法 构造器(只有一个构造器时候, 可以省略)

  • @Resource: 属性 方法

  • 注解来源不同:

  • @Autowired Spring 提供

  • @Resource Java提供

  • @Autowired 包含必须输入功能!

推荐使用 @Autowired !!!

组合注解和 Meta注解(元注解)


Java 中多个注解可以组合拼接为一个新注解

  • 拼接的新注解成为 组合注解

  • 没有参与拼接的注解成为 元注解

  • 新注解具备全部全部元注解的功能

@Service注解的源码:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component

public@interfaceConfiguration {

@AliasFor(annotation=Component.class)

Stringvalue() default"";

}

为何Spring搞组合注解?为了表示合理的业务意义:

  • Component : 组件

  • Configuration: 配置

  • Service: 服务

  • Controller: 控制器

组合注解都有和 @Component 相同的功能:

@Service

publicclassUserService {

@Override

publicStringtoString() {

return"UserService";

}

}

@SpringBootTest

publicclassUserServiceTests {

Loggerlogger=LoggerFactory.getLogger(UserServiceTests.class);

@Autowired

UserServiceuserService;

@Test

voiduserService(){

logger.debug("Service {}", userService);

}

}

Spring 提供了创建Bean组件的组合注解

这些组合注解都有和 @Component 类似的功能

  • @Service 用在业务层组件

  • @Repository 用在数据访问层组件

  • @Controller 和 @RestController 用在 Spring MVC 控制器

  • @Configuration 用在Spring Java 配置累上

甚至可以自定义自己的组合注解。

@Component

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public@interfaceMyService {

}

@MyService

publicclassUserService {

@Override

publicStringtoString() {

return"UserService";

}

}

注意: 组合注解和元注解都是相对概念, 一个组合注解也可以作为元注解,再次组合为新注解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值