读 Spring Boot实战笔记--day001

Spring java bean 配置
Java 配置是通过 @Configuration和 @Bean来实现的:

  1. @Configuration声明当前类是一个配置类,相当于一个 Spring 配置的xml文件。
  2. @Bean注解在方法上,声明当前方法的返回值为一个 Bean。

如下:此处没有使用@Service声明 Bean。使用功能类的 Bean。

//1
package com.example.springboot;

/**
 * @Author: hyh
 * @Date: 2021/10/7 11:31
 **/
public class Bean1 {

    public String say(){
        return "Hello";
    }
}
//2
package com.example.springboot;

/**
 * @Author: hyh
 * @Date: 2021/10/7 11:31
 **/
public class Bean2 {
    Bean1 bean1;

    public void setBean1(Bean1 bean1) {
        this.bean1 = bean1;
    }

    public String say(){
        return bean1.say();
    }
}
//3
package com.example.springboot;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: hyh
 * @Date: 2021/10/7 11:34
 * 使用@Configuration注解表明当前类是一个配置类,这意味着这个类里可能有0个或者多个@Bean注解,
 * 此处没有使用包扫描,是因为所有的Bean都在此类中定义了。
 **/
@Configuration
public class BeanConfig {
    //  使用@Bean 注解声明当前方法bean1 的返回值是一个 Bean,Bean的名称是 方法名
    @Bean
    public Bean1 bean1(){
        return new Bean1();
    }

    @Bean
    public Bean2 bean2(){
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1());
        return bean2;
    }
    
 	// 通过 bean 注解返回实例对象,同时可将bean 实例作为参数传递
    @Bean
    public Bean2 bean2(Bean1 bean1){
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1);
        return bean2;
    }
}
//4 测试
package com.example.springboot;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

/**
 * @Author: hyh
 * @Date: 2021/10/6 15:12
 **/
@Configuration
@ComponentScan("com.example.springboot")
public class Test {
    public static void main(String[] args) {
        // 接收一个配置类的容器
        AnnotationConfigApplicationContext context 
        					= new AnnotationConfigApplicationContext(Test.class);
        Bean2 bean = context.getBean(Bean2.class);
        System.out.println(bean.say());
    }
}
输出:
Hello

AOP
AOP:面向切面编程,相对于OOP面向对象编程。
Spring 的 AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在 OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。
Spring支持 AspectJ的注解式切面编程:

  1. 使用@Aspect声明是一个切面。
  2. 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
  3. 其中@After、@Before、@Around参数的拦截规则为切点(PointCut),为了使切点复用,可使用
  4. @PointCut专门定义拦截规则,然后在@After、@Before、@Around的参数中调用。

其中符合条件的每一个被拦截处为连接点(JoinPoint)。

// 1 切面注解定义
package com.example.springboot;

import java.lang.annotation.*;

/**
 * @Author: hyh
 * @Date: 2021/10/7 14:19
 **/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAop {
    String log();
}
// 2 要切的方法上加上注解
package com.example.springboot;

import org.springframework.stereotype.Service;

/**
 * @Author: hyh
 * @Date: 2021/10/7 14:22
 **/
@Service
public class LogService {

    @LogAop(log = "添加")
    public void add(){
        System.out.println("执行了添加");
    }
}
// 定义切面进行逻辑处理
package com.example.springboot;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Author: hyh
 * @Date: 2021/10/7 14:24
 **/
@Aspect
@Component
public class LogAspect {

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.example.springboot.LogAop)")
    public void pointCut() {
    }

//    // 配置连接点 方法开始执行时通知
//    @Before(value = "pointCut()")
//    public void beforeLog(JoinPoint joinPoint) {
//        System.out.println("开始执行前置通知");
//    }
//    //    方法执行完后通知
//    @After(value = "pointCut()")
//    public void afterLog(JoinPoint joinPoint) {
//        System.out.println("开始执行后置通知");
//    }
//
//    //    抛出异常后通知
//    @AfterThrowing(value = "pointCut()")
//    public void afterThrowingLog(JoinPoint joinPoint) {
//        System.out.println("方法抛出异常后执行通知");
//    }
//
//    //    环绕通知
//    @Around(value = "pointCut()")
//    public void aroundLog(ProceedingJoinPoint  joinPoint) throws Throwable {
//        System.out.println("环绕通知开始 日志记录");
//        Object proceed = joinPoint.proceed();
//        System.out.println("环绕通知结束 日志记录");
//    }

    /**
     *  连接点执行完成后 执行此方法  如果抛出异常不执行
     * @param joinPoint
     */
    @After(value = "pointCut()")
    public void AddLog(JoinPoint joinPoint){

        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();

        // 获取方法名
        String methodName = method.getName();
        // 获取操作
        LogAop opLog = method.getAnnotation(LogAop.class);
        String log = opLog.log();
        System.out.println(methodName + ": "+ log);

    }
}

Spring Scope

Scope描述的是Spring 容器如何新建 Bean的实例的。Spring 的 Scope有以下几种,通过@Scope注解来实现。

  1. Singleton:一个Spring 容器中只有一个 Bean的实例,此为 Spring
    的默认配置,全容器共享一一个实例。(单例)
  2. Prototype:每次调用新建一个 Bean 的实例。 (多实例)
/**
 * @Author: hyh
 * @Date: 2021/10/7 14:22
 **/
@Service
@Scope("prototype") // 不写默认为单例
public class LogService {

    @LogAop(log = "添加")
    public void add(){
        System.out.println("执行了添加");
    }
}

Spring EL 和资源调用
Spring 开发中经常涉及调用各种资源的情况,包含普通文件、网址、配置文件、系统环境变量等,我们可以使用 Spring的表达式语言实现资源的注入。
Spring主要在注解@Value的参数中使用表达式。

package com.example.springboot;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import java.io.IOException;

/**
 * @Author: hyh
 * @Date: 2021/10/6 11:24
 **/
@Configuration
@ComponentScan("com.example.springboot")
@PropertySource("classpath:application.properties")
public class ELDemo {

    @Value("注入字符串")
    private String name1;


    @Value("#{T(java.lang.Math).random() * 100}")
    private String name3;

    @Value("${book.author}")
    private String name4;

    @Value("www.baidu.com")
    private Resource name5;

    @Value("classpath:application.properties")
    private Resource name6;

    @Autowired
    private Environment e;

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfig(){
        return new PropertySourcesPlaceholderConfigurer();
    }

    public void print() throws IOException {
        System.out.println(name1);
        System.out.println(name3);
        System.out.println(name4);
        System.out.println(name5);
        System.out.println(IOUtils.toString(name6.getInputStream()));
        System.out.println(e.getProperty("book.author"));
    }
}
package com.example.springboot;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: hyh
 * @Date: 2021/10/6 15:12
 **/
@Configuration
@ComponentScan("com.example.springboot")
public class Test {


    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new 
        						AnnotationConfigApplicationContext(Test.class);
        ELDemo bean = context.getBean(ELDemo.class);
        bean.print();
        context.close();


    }
}

输出:
注入字符串
18.311029337659402
wangfei
class path resource [www.baidu.com]
book.author=wangfei
book.name=Spring Boot

15:33:41.096 [main]DEBUG org.springframework.core.env.PropertySourcesPropertyResolver
 -Found key 'book.author' in PropertySource
  'class path resource [application.properties]'with value of type String
wangfei


Bean 的初始化和销毁
在我们实际开发的时候,经常会遇到在 Bean 在使用之前或者之后做些必要的操作,Spring对Bean的生命周期的操作提供了支持。在使用Java配置和注解配置下提供如下两种方式:

  1. Java 配置方式:使用@Bean的 initMethod和destroyMethod(相当于xml配置的init-method和 destory-method)。
  2. 注解方式:利用JSR-250的@PostConstruct 和@PreDestroy。
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>jsr250-api</artifactId>
    <version>1.0</version>
</dependency>

package com.example.springboot;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @Author: hyh  用注解方式
 * @Date: 2021/10/27 15:45
 **/
public class BeanWay {

    // 在构造函数执行完之后
    @PostConstruct
    public void init(){
        System.out.println("init");
    }
    public void doing(){
        System.out.println("doing");
    }

    // 在Bean 销毁之前
    @PreDestroy
    public void destroy(){
        System.out.println("destroy");
    }
}

package com.example.springboot;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: hyh
 * @Date: 2021/10/27 15:50
 **/
@Configuration
public class BeanWayConf {

    @Bean
    public BeanWay bean1(){
        return new BeanWay();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
        			AnnotationConfigApplicationContext(BeanWayConf.class);
        BeanWay bean1 = context.getBean(BeanWay.class);
        bean1.doing();
        context.close();
    }
}
输出:
init
doing
destroy
--------------------------------------------------------------------------------

package com.example.springboot;

/**
 * @Author: hyh  用 java配置方式
 * @Date: 2021/10/27 15:45
 **/
public class BeanWay {

    // 在构造函数执行完之后
    public void init(){
        System.out.println("init");
    }
    public void doing(){
        System.out.println("doing");
    }

    // 在Bean 销毁之前
    public void destroy(){
        System.out.println("destroy");
    }
}
package com.example.springboot;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: hyh
 * @Date: 2021/10/27 15:50
 **/
@Configuration
public class BeanWayConf {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public BeanWay bean1() {
        return new BeanWay();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
        					AnnotationConfigApplicationContext(BeanWayConf.class);
        BeanWay bean1 = context.getBean(BeanWay.class);
        bean1.doing();
        context.close();
    }
}
输出:
init
doing

destroy

Profile
Profile为在不同环境下使用不同的配置提供了支持(开发环境下的配置和生产环境下的配置肯定是不同的,例如,数据库的配置)。

  1. 通过设定Environment的ActiveProfiles 来设定当前context需要使用的配置环境。在开发中使@Profile注解类或者方法,达到在不同情况下选择实例化不同的 Beano
  2. 通过设定jvm的spring.profiles.active参数来设置配置环境。
  3. Web项目设置在Servlet的 context parameter 中。
package com.example.springboot;

import lombok.Data;

/**
 * @Author: hyh
 * @Date: 2021/10/7 11:31
 **/
@Data
public class Bean1 {

    private String str;

    public String say() {
        return "str";
    }

    public Bean1(String str){
        this.str = str;
    }
    public Bean1( ){
    }

}

package com.example.springboot;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

/**
 * @Author: hyh
 * @Date: 2021/10/30 10:53
 **/
@Configuration
public class ProfileConfig {


    @Bean
    @Profile("dev")
    public Bean1 ben1(){
        return new Bean1("dev");
    }

    @Bean
    @Profile("test")
    public Bean1 ben2(){
        return new Bean1("test");
    }
}
package com.example.springboot;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: hyh
 * @Date: 2021/10/6 15:12
 **/
@Configuration
@ComponentScan("com.example.springboot")
public class Test {


    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new 
        									AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.register(ProfileConfig.class);
        context.refresh();  //需要刷新容器
        Bean1 bean1 = context.getBean(Bean1.class);
        System.out.println(bean1.getStr());
        context.close();
    }
}
输出:
dev

事件
Spring的事件(Application Event)为Bean与 Bean 之间的消息通信提供了支持。当一-个Bean处理完一个任务之后,希望另外一个 Bean知道并能做相应的处理,这时我们就需要让另外一个 Bean 监听当前Bean所发送的事件。
Spring 的事件需要遵循如下流程:
(1) 自定义事件,集成ApplicationEvent。
(2)定义事件监听器,实现ApplicationListener。
(3)使用容器发布事件。

package com.example.springboot.event;

import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;

/**
 * @Author: hyh
 * @Date: 2021/11/1 9:19
 **/
@Setter
@Getter
public class MyEvent extends ApplicationEvent {

    private String msg;

    public MyEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }
}
package com.example.springboot.event;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @Author: hyh
 * @Date: 2021/11/1 9:23
 **/
@Component
public class MyEvListener implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        String msg = event.getMsg();
        System.out.println("监听到消息:" + msg);
    }

}
package com.example.springboot.event;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;



/**
 * @Author: hyh
 * @Date: 2021/11/1 14:58
 **/
@Component
public class MyPublish {

    @Autowired
    ApplicationContext ac;

    public void send (String msg){
        ac.publishEvent(new MyEvent(this,msg));
    }
}
package com.example.springboot;

import com.example.springboot.event.MyPublish;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: hyh
 * @Date: 2021/10/6 15:12
 **/
@Configuration
@ComponentScan("com.example.springboot.event")
public class Test {


    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
        							AnnotationConfigApplicationContext(Test.class);
        MyPublish bean1 = context.getBean(MyPublish.class);
        bean1.send("发送消息");
        context.close();
    }
}
输出:
监听到消息:发送消息

下一章:day002

你可能感兴趣的:(Spring,spring,boot,spring,java)