实习修炼之第四天

概述

Service层 Web层 Spring注释

Service层

Java读取property配置文件
后缀为“.properties”的文件在Java中被称为属性文件,在Java项目中一些配置参数保存在Property文件中,这样能保证不修改原代码直接修改Property文件。所以说配置文件可维护性好,会让程序中变化的地方很灵活的配置。

spring boot项目application.properties
一般来说,由于应用环境的不同,每个环境下的配置略有不同,所以根据这个创建多个配置文件,如下图所示,创建spring boot项目后可以同时创建多个.properties文件,只要符合它要求的格式(application-{profile}.properties)即可,其中{profile}是变量用于自定义配置文件名称。
在这里插入图片描述
System.setProperty 与 System.getProperty、java的系统属性
在java应用程序运行时,特别是需要在跨平台工作环境中运行时,需要确定操作系统的类型、用户JDK版本、用户工作目录等随工作平台变化的信息,以确保程序正确运行。这些程序的运行环境信息为java平台的自身配置,被称为java的系统属性。系统属性与操作系统的环境变量类似,可以认为是JVM虚拟机的环境变量。(其实JVM本身就相当于一个计算机,自然它本身也需要一些自己的环境变量 ,JVM中称为系统属性)

System.getProperties()可以确定当前的系统属性,返回值是一个Properties;
System.load(String filename)等同于:System.getProperties().load(String filename)它们的作用是可以从作为动态库的本地文件系统中指定的文件名加载代码文件。
System.setProperties(Properties propes):将系统属性设置为Properties参数;
System.setProperties(String key,String
value)等同于System.getProperties().setProperties(String key,String value):设置指定键指示的系统属性

系统属性与环境变量都是名称与值之间的映射。两种机制都可以将用户定义的信息传递给java进程。全局变量产生的更多的是全局效应,他们不仅对java子进程可见,而且对于定义它们的所有子进程都是可见的。程序中尽可能使用系统属性,而环境变量应该在全局范围需要时才使用。

ShutdownHooks源码详解、RegisterShutDown

  • ShutdownHooks*
    首先来说, ShutdownHooks是用来做一些资源清除的操作,比如防止应用关闭后线程池仍然存活从而造成服务宕机。所以它被称为关闭钩子,这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作。它是关闭jvm的一个直接类,其属性元素如下所示
class Shutdown {  //不能直接调用此类。通常是通过Runtime.getRuntime().addShutdownHook(thread)间接使用。
    
    // shutdown的几个状态
    private static final int RUNNING = 0; // application正在运行
    private static final int HOOKS = 1; // 正在处理hooks
    private static final int FINALIZERS = 2; // 正在处理finalizers
    private static int state = RUNNING; // 默认为运行状态
    
    // hook的数量,10个。
    private static final int MAX_SYSTEM_HOOKS = 10;
    private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
    private static int currentRunningHook = 0;
     }
  • RegisterShutDown*
    RegisterShutDown是用来在非web应用中关闭IoC容器的(转载自 http://www.codes51.com/itwd/3121940.html ),我在学习中用到的registerShutdownHook代码如下所示

    public void setRegisterShutdownHook(boolean registerShutdownHook) {
    this.registerShutdownHook = registerShutdownHook;
    }

SpringApplication
SpringApplication类的直接作用是在main方法中通过自有的run方法启动spring应用,具体参考 https://www.cnblogs.com/red-code/p/9267106.html
其主要用的是run方法,初始化代码如下

private void initialize(Object[] sources) {
   if (sources != null && sources.length > 0) {
       this.sources.addAll(Arrays.asList(sources));
   }
    this.webEnvironment = deduceWebEnvironment();  
  	setInitializers((Collection) getSpringFactoriesInstances(
	ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();

}

Web层

下面是一个web层主程序

@Configuration
@ComponentScan("com.zhicong.trade.web.controller")
@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
public class Bootstrap extends WebMvcConfigurationSupport {

    public static void main(String[] args) {
        try {
            long start = System.currentTimeMillis();
            SpringApplication               app         = new SpringApplication(Bootstrap.class, "classpath*:/spring/*.xml");
            ConfigurableApplicationContext  appContext  = app.run(args);
            Bootstrap                       boot        = appContext.getBean(Bootstrap.class);
            List<HttpMessageConverter<?>>   converts    = boot.getMessageConverters();
            HeaderHttpMessageConverter      convert     = appContext.getBean(HeaderHttpMessageConverter.class);
            // 清除无效convert(非必须)
            converts.clear();
            // 添加唯一的convert , 避免多余的检测
            converts.add(convert);
            long end = System.currentTimeMillis();

            System.out.println("trade-web started in " + (end - start) + "ms .");
        }catch (Exception e){
            e.printStackTrace();
            System.exit(0);
        }
    }

}

上述代码是对程序的运行环境做一个准备,web层具体的与用户交互的响应过程如下所示
首先浏览器发送请求,请求到达web服务器(比如apache服务器),然后到达servlet容器(比如tomcat),然后就到springmvc的DispatcherServlet,当然此时数据的表现形式已经早就不是url了,是对象了(万物皆对象,java的金句)。然后就开始springmvc开始工作了,具体有以下几步:
参考 https://blog.csdn.net/qq_39385706/article/details/78166529

1 第一步DispatcherServlet调用映射处理器handleMapping,通过url找到对应的controller类(比如示例代码中的@RequestMapping(“virtualPrice”),通过此时的virtualPrice通就可以找到对应的controler类)以及HandlerMapping接口的实现(代码见注释部分学习)
DispatcherServlet调用HandlerAdapter来执行业务的方法,传一个handler返回一个modelandview。
这两步骤完成后就就完成了Handler的真正调用过程,最终调用过程是返回一个ModelAndView对象,然后就到了视图解析器找到对应的view的,然后把模型的数据渲染到视图中,然后把视图返回会浏览器。

HandlerMapping接口源代码

public interface HandlerAdapter {  
    boolean supports(Object handler);  
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;  
    long getLastModified(HttpServletRequest request, Object handler);   
}  

ConfigurableApplicationContext
在使用Spring的时候,我们经常需要先得到一个ApplicationContext对象,然后从该对象中获取我们配置的Bean对象。ApplicationContext隶属于org.springframework.context,是SpringFramework中Bean的管理者,为SpringFramework的诸多功能提供支撑作用。

ConfigurableApplicationContext接口结合了所有ApplicationContext需要实现的接口。因此大多数的ApplicationContext都要实现此接口。它在ApplicationContext的基础上增加了一系列配置应用上下文的功能。配置应用上下文和控制应用上下文生命周期的方法在此接口中被封装起来,以免客户端程序直接使用。
参考 https://blog.csdn.net/weixin_39165515/article/details/77169231

在项目中用到的是下面代码,用于确定上下文环境(个人认为,上下文环境应该是程序运行所使用的各种资源)。

SpringApplication               app         = new SpringApplication(Bootstrap.class, "classpath*:/spring/*.xml");
ConfigurableApplicationContext  appContext  = app.run(args);
Bootstrap                       boot        = appContext.getBean(Bootstrap.class);
List<HttpMessageConverter<?>>   converts    = boot.getMessageConverters();
HeaderHttpMessageConverter      convert     = appContext.getBean(HeaderHttpMessageConverter.class);

同时,对于java web用户请求至页面响应的过程可学习 https://blog.csdn.net/zhangxg_cq/article/details/81120056

Spring注释

注释实现原理
Java5.0以后引入了注解的概念,注解只在源码阶段保留(编译时忽略),不会影响程序内部的东西,决定运行级别是一个标识,且定义一个注解就可以在其他类中使用。注解使我们可以在稍后某个时刻方便的使用这些数据(通过解析注解来使用这些数据),用来将任何的信息或者元数据与程序元素(类、方法、成员变量等)进行关联。
注解(Annotation)其实是一种接口。通过Java的反射机制相关的API来访问Annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。Java语言解释器在工作时会忽略这些Annotation,因此在JVM中这些Annotation是“不起作用”的,只能通过配套的工具才能对这些Annotation类型的信息进行访问和处理。
谈到注解就要了解一下元注解,元注解就是注解注解的注解,java里有以下几个元注解

  1. @Target注解:描述注解的使用范围(即:被修饰的注解可以用在什么地方,其取值范围定义在ElementType 枚举中)
  2. @Reteniton注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时),一共有三种策略,定义在RetentionPolicy枚举中,如下所示:

    SOURCE, // 源文件保留
    CLASS, // 编译期保留,默认值
    RUNTIME // 运行期保留,可通过反射去获取注解信息

  3. @Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
  4. @Inherited注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。

下面是项目中自己实现的一个注解的源代码,可参考记忆学习


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
    @AliasFor("path")
    String[] value() default {};
    @AliasFor("value")
    String[] path() default {};
    RequestMethod[] method() default {};
    String[] params() default {};
    String[] headers() default {};
    String[] consumes() default {};
    String[] produces() default {};
}

其次,对于自定义注解来说,参数设置一般是
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
  第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号。

项目中常用的Spring注解

  1. @RequestMapping注释
    RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示 类中的所有响应请求的方法都是以该地址作为父路径。RequestMapping注解有六个属性。
    可参考 https://www.cnblogs.com/qq78292959/p/3760560.html 博客学习。
    默认RequestMapping("…str…")即为value的值;
    这一注解在web层很是有用,可以使得容器根据确定要调用的controler从而实现对应业务,项目中有一部分虚拟商品定价格的业务代码如下所示,就是利用这一注解的地址映射实现虚拟商品定价功能。
/**
 * 虚拟商品定价
 */
@RequestMapping("virtualPrice")
@ResponseBody
@AutoLogger
@SuppressWarnings("unchecked")
public ServiceResponse<VirtualTradePriceResponse> virtualPrice(@RequestBody BuyVirtualRequest request) {
    TradeItem item = request.getItem();
    GoodsForTrade goods;
    try {
        checkVirtualTradeRequest(request);
        goods = goodsManager.getFissionById(item.getFissionId(), item.getSkuCode(), request.getUserId());
        if (goods == null) {
            throw new TradeException(ServiceResponse.newResponse(request, TradeError.ITEM_NOT_EXIST));
        }
        if (!goods.isVirtual()) {
            throw new TradeException(ServiceResponse.newResponse(request, TradeError.ITEM_NOT_EXIST.getCode(), "非虚拟商品请走普通商品购买通道."));
        }
    } catch (TradeException e) {
        return e.getResponse();
    }

    VirtualTradePriceResponse response = new VirtualTradePriceResponse();
    response.setItemPrice(goods.getSellPrice() * item.getNum());
    response.setPaymentPrice(response.getItemPrice());
    return ServiceResponse.newResponse(request, CommonError.SUCCESS, response);
}
  1. @Resource 注解的使用
    在前面第二篇笔记里简单介绍过,具体可参考 https://blog.csdn.net/weixin_38237873/article/details/83650429 学习。
  2. @RequestBody注解
    此注解是通过使用HandlerAdapter 配置的HttpMessageConverters来解析post data body,然后绑定到相应的bean上的。它作用在形参列表上,换句话说,该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析, 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。阅读其源码如下所示

/**
 * Annotation indicating a method parameter should be bound to the body of the web request.
 * The body of the request is passed through an {@link HttpMessageConverter} to resolve the
 * method argument depending on the content type of the request. Optionally, automatic
 * validation can be applied by annotating the argument with {@code @Valid}.
 *
 * <p>Supported for annotated handler methods in Servlet environments.
 *
 * @author Arjen Poutsma
 * @since 3.0
 * @see RequestHeader
 * @see ResponseBody
 * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
 * @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {

	/**
	 * Whether body content is required.
	 * <p>Default is {@code true}, leading to an exception thrown in case
	 * there is no body content. Switch this to {@code false} if you prefer
	 * {@code null} to be passed when the body content is {@code null}.
	 * @since 3.2
	 */
	boolean required() default true;
}

与其对应的是**@ResponseBody**注解,@ResponseBody是作用在方法上的,@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用(也就是AJAX)。如下面例子在使用 @RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。

@RequestMapping(value = "user/login")
@ResponseBody
public User login(@RequestBody User user) {   
    return user;    
}

其中response注释源码及其含义如下所示

/**
 * Annotation that indicates a method return value should be bound to the web
 * response body. Supported for annotated handler methods in Servlet environments.
 *
 * <p>As of version 4.0 this annotation can also be added on the type level in
 * which case it is inherited and does not need to be added on the method level.
 *
 * @author Arjen Poutsma
 * @since 3.0
 * @see RequestBody
 * @see RestController
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {

}

具体进一步学习深入,参考对参数和 Content-Type的介绍https://blog.csdn.net/qq_34500957/article/details/80523200

  1. @Data 注解
    此注解注解在类上, 为类提供读写属性, 此外还提供了 equals()、hashCode()、toString() 方法。
    需要注意的是,这个注解来自于 lombok,lombok 能够减少大量的模板代码,减少了在使用@Data 注解时, 需要导入lombok.Data。

补充

dubbo原码阅读
点击此处传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值