Spring:IOC控制反转、@Bean和@Component、日志、注入、注解

Spring 核心功能:IOC(控制反转)和AOP( 面向切面编程)

一、什么是IOC(Inversion of Control) 控制反转

1.主动控制:

应用程序主动控制管理对象组件

2.控制反转:

由外部容器/框架创建管理对象组件交给应用程序使用

二、使用原生Spring创建Demo项目

1.导入Maven包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.20</version>
</dependency>

2.创建配置类,组件扫描

@Configuration
@ComponentScan("com.demo.spring")
public class ContextConfig {
	@Bean//启动时加载
	public DemoBean bean(){//BeanID:bean
		return new DemoBean();
	}
}

3.创建Demo类

@Component
public class DemoBean {
    @Override
    public String toString() {
        return "Hello World";
    }
}

4.创建Spring容器(应用程序上下文)

public class Application {
    public static void main(String[] args) {
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ContextConfig.class);

        DemoBean demoBean = context.getBean("bean",DemoBean.class);
        System.out.println(demoBean);
    }
}

创建完成!

5.分析

在这里插入图片描述
@SpringBootApplication注解知多少:

  • @SpringBootApplication 是组合注解, 是由元注解组合而成
  • 组成组合注解中的每个注解称为元注解
  • 使用多个元注解标注代码和使用组合注解标注代码作用一样

举例

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { 
   							@Filter(type = FilterType.CUSTOM, 
   									classes = TypeExcludeFilter.class),
   							@Filter(type = FilterType.CUSTOM, 
   									classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

三、JavaBean

1.JavaBean遵守如下规范:

  1. 需要定义package
  2. 存在无参构造器
  3. 需要实现序列化接口
  4. 包含使用getXxx setXxx声明的"Bean属性(Bean Property)
    a. Bean属性是指serXxx getXxx方法
    b. 对象属性(Object Filed)是指对象的实例变量

举例:

package com.demo.spring
public class Person implements Serializable{
	private String name = "hello Bean";//实例变量,对象属性
	public String getName(){//name
		return name;
	}
	public void setName(String name){//name
		this.name = name;
	}
	public String getPreferName(){//preferName
		return name.substring(0,1);
	}
}

2.JavaBean的作用域(JavaBean的有效范围)

JavaBean默认情况下是单例的(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配置服务器*

3.组件@Component扫描示例

SingletonBean .java

@Component
@Scope("singleton")
public class SingletonBean {
    @Override
    public String toString() {
        return "SingletonBean";
    }
}

PrototypeBean.java

@Component
@Scope("prototype")
public class PrototypeBean {
    @Override
    public String toString() {
        return "PrototypeBean";
    }
}

测试类ScopeTests .java

@SpringBootTest
public class ScopeTests {
    Logger logger = LoggerFactory.getLogger(ScopeTests.class);

    @Resource
    //@Autowired
    ApplicationContext context;

    @Test
    void test(){
        SingletonBean bean1 = context.getBean(SingletonBean.class);
        SingletonBean bean2 = context.getBean(SingletonBean.class);
        logger.debug("单例 {}", bean1 == bean2);//单例 true
        PrototypeBean bean3 = context.getBean(PrototypeBean.class);
        PrototypeBean bean4 = context.getBean(PrototypeBean.class);
        logger.debug("原形 {}", bean3 == bean4);//原形 false
    }

    @Test
    void single(){
        Single single = context.getBean(Single.class);
        logger.debug("{}", single);//com.demo.bean.Single@40021799
    }
}

4.显式声明@Bean示例

ScopeConfig.java

@Configuration
public class ScopeConfig {

    @Bean
    public List<String> provinceNames() {
        List<String> provinceNames = new ArrayList<>();
        provinceNames.add("北京");
        provinceNames.add("上海");
        return provinceNames;
    }

    @Bean
    @Scope("prototype")//设置为原形
    public List<String> holidayNames() {
        List<String> list = new ArrayList<>();
        list.add("中秋");
        list.add("国庆");
        return list;
    }
}

测试类ScopeTests .java

@SpringBootTest
public class ScopeTests {
    Logger logger = LoggerFactory.getLogger(ScopeTests.class);

    @Resource
    //@Autowired
    ApplicationContext context;
    
    @Test
    void scopeTests(){
        List<String> list1 = context.getBean("provinceNames", List.class);
        List<String> list2 = context.getBean("provinceNames", List.class);
        logger.debug("单例 {}", list1==list2);//单例 true
        List<String> list3 = context.getBean("holidayNames", List.class);
        List<String> list4 = context.getBean("holidayNames", List.class);
        logger.debug("原形 {}", list3==list4);//原形 false

    }
}

四、SpringBoot中使用日志(LoggerFactory)

1.为什么使用日志系统

  • System.out.println() 是IO操作,性能很差,使用过多会严重影响系统性能
  • 日志系统提供了高性能缓存机制,有着很好的性能,并且可以使用配置进行统一打开和关闭
  • Spring Boot 整合了日志系统,简单配置就可以使用

2.使用日志系统

private static Logger logger = LoggerFactory.getLogger(LoggerTests.class);
//static静态方法调用
//Logger和LoggerFactory 来自org.slf4j包,使用@slf4j注解也是同样的效果,但一般不用
//方法参数是当前的类名,用于设置Logger的层级,方便分组管理,分组打开和关闭

3.设置日志门槛

  • 在application.properties中加入该语句:
logging.level.cn.tedu.springioc = debug
//默认的日志级别是info
//功能:该包和子包中的日志输出语句高于debug级别的信息会被输出。
//可以粗粒度设置到包,或者细粒度的类

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

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

五、组件扫描隐式创建对象@Component

1.显式 & 隐式

显式隐式
强制类型转换, 需要明确使用类型转换运算符"(类型)" 明确说明在计算自动类型转换, 不需要写任何代码, 就会自动发生

在Spring中是怎么显式创建组件的:

  • @Bean注解
  • 在配置类中创建Java对象
  • 明确使用new运算创建了对象

隐式创建对象:

  • @Component注解
  • @Autowired注入组件
  • SpringBoot启动时自动创建组件
    示例:
    创建一个配置类

@Configuration
public class ConfigBeans {

   /**
    * 使用@Bean 显示声明Java Bean 组件
    * Bean ID 为 names
    * @return 创建的JavaBean
    */
   @Bean
   public ArrayList<String> names(){
      ArrayList<String> names = new ArrayList<>();
      names.add("Tom");
      names.add("Jerry");
      return names;
   }

   @Bean
   public ArrayList<String> mobilePhone(){
      ArrayList<String> list = new ArrayList<>();
      list.add("110");
      list.add("119");
      return list;
   }

   /**
    * 使用 @Bean的属性设置BeanID
    * @return 城市列表
    */
   @Bean("cities")
   public ArrayList<String> list(){
      ArrayList<String> list = new ArrayList<>();
      list.add("北京");
      list.add("上海");
      return list;
   }
}

创建一个测试类

@SpringBootTest
public class BeanTests {

   Logger logger = LoggerFactory.getLogger(BeanTests.class);

   @Autowired
   @Qualifier("cities")
   ArrayList<String> names;

   @Test
   void names() {
      logger.debug("{}", names);
   }
}

六、SpringBoot自动组件扫描

1.使用@Component组件扫描注意事项

  1. 如果 Java Bean 没有定义在当前包和子包中则扫描不到
  2. 如果 Bean 创建到其他位置(不是当前包和子包)则需要手动配置组件扫描
  3. 如果在启动类上配置了组件扫描,则会关闭自动组件扫描
  4. @Component 注解需要和 @ComponentScan 注解一起使用才可以
  5. 注意精确扫描范围,提升扫描效率

2.举例

ExampleBean.java

package com.test.bean;

import org.springframework.stereotype.Component;

@Component
public class ExampleBean {
    @Override
    public String toString() {
        return "exampleBean";
    }
}

ExampleBeanTests.java

@ComponentScan({"com.test.service","com.test.bean"})
@SpringBootTest
public class ExampleBeanTests {

    Logger logger = LoggerFactory.getLogger(ExampleBeanTests.class);

    @Autowired
    ExampleBean exampleBean;

    @Test
    void exampleBean(){
        logger.debug("{}", exampleBean);
    }

}

七、区分@Bean和@Component

@Bean(显式配置)

优点缺点
集中在一个(或几个)地方,方便统一管理Bean比@Component注解更加冗余,繁琐
编写任何需要的代码,在配置类中自定义初始化程序
可以对配置类进行单元测试,可以单独测试
适用于所有类

@Component(隐式配置)

优点缺点
编辑位置明确,就在类中配置分布在代码库中
方便快速开发,直接标注在源码上难以调试/维护
只适用于自己的代码

八、@Autowired注入和@Resource注入

1.@Autowired注入

(1)注入方式
  • 构造方法注入(推荐):必须存在唯一的匹配类型的依赖,只有一个构造器时候可以省略
  • set方法注入:构造方法注入和setter注入均支持多参数注入
  • 字段注入(对象属性注入):private字段也可以注入
(2)@Autowired依赖可以使用requied覆盖
@Autowired(required=false)
(3)注入机制

类型匹配和name匹配
在这里插入图片描述

**注:**添加@Qualifer注解指定beanID
Worker.java

@Component
public class Worker {
    Logger logger = LoggerFactory.getLogger(Worker.class);
    private String name = "Chloe";

    @Autowired
    @Qualifier("crayon")
    private Tool tool;

    public Worker() {
    }

    public void work(){
        logger.debug("{}use{}painting", name, tool);
    }
}

测试类DITests.java

@SpringBootTest
public class DITests {

    @Autowired
    Worker worker;

    @Test
    void worker() {
        worker.work();
    }
}

2.@Resource

@Resource来自JSR-250,能被EJB 3.0和Spring支持。默认根据属性/字段的名称,而不是默认按类型来识别依赖项,名称是Spring的Bean名称,找不到匹配的name,则直接回退到根据类型注入,且仅支持Setter和字段注入

3.@Autowired 和 @Resource区别

注入匹配规则不同支持的注入方式不同注解来源不同包含输入功能
@Autowired先匹配类型, 再匹配名称属性 方法 构造器(只有一个构造器时候, 可以省略)Spring 提供
@Resource先匹配名称, 再匹配类型属性 方法Java提供(JSR-250)

九、元注解

  • 拼接的新注解成为 组合注解
  • 没有参与拼接的注解成为 元注解
  • 组合注解都有和 元注解相同的功能

举例一:

在这里插入图片描述
贴上@Configuration注解的源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
   @AliasFor(annotation = Component.class)
   String value() default "";
}

在这里插入图片描述

举例二:

在这里插入图片描述
这些组合注解都有和 @Component 类似的功能

  • @Service 用在业务层组件
  • @Repository 用在数据访问层组件
  • @Controller 和 @RestController 用在 Spring MVC 控制器
  • @Configuration 用在Spring Java 配置类上

也可以自定义自己的组合注解
创建:

@Component
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyService {
}

UserService.java

@MyService
public class UserService {
    @Override
    public String toString() {
        return "UserService";
    }
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值