• [技术干货] spring
    IOC本质控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,在以往的代码中,对象的创建由程序自己控制,控制反转后将对象的创建交由springIOC容器创建和管理,需要使用时从容器中拿到使用。控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。spring快速构建导入jar包<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-webmvc</artifactId></dependency>新建实体类public class Hello {    private String name;     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }     public void show() {        System.out.println("Hello," + name);    }}在resourse下创建beans.xml<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">    <!--bean就是java对象 , 由Spring创建和管理-->    <bean id="hello" class="com.lyj.bean.Hello">        <property name="name" value="Spring"/>    </bean></beans>测试ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");        //getBean : 参数即为spring配置文件中bean的id .        Hello hello = (Hello) context.getBean("hello");        hello.show();通过这种方式,就可以在xml中创建和管理一个id名为hello的,类型为Hello的的实体类对象实例,在Java代码中通过方法直接获取这个实例。
  • [技术干货] Spring Boot集成log4j日志
    一、 引入log4j依赖在创建Spring Boot工程时,我们引入了spring-boot-starter, 该依赖 默认的日志框架Logback,所以我们在引入log4j之前,需要先排除该包的依赖,再引入log4j的依赖:二、 配置log4j.properties在引入了log4j依赖之后,只需要在src/main/resources目录下加入log4j.properties配置文件,就可以开始对应用的日志进行配置使用。开发环境 控制台输出
  • [技术干货] springboot2.0整合logback
    一、开发环境开发环境• JDK 8• Spring Boot 2.0. 9 RELEASE• Maven• Windows 10• IDEA 20 20 . 3Spring Boot默认集成了Logback,可以开箱即用, 不需要再次引入 spring-boot-starter-logging 依赖 , 因为 Spring Boot启动 时加载 spring-boot-starter ,而 spring-boot-starter 包含了 spring-boot-starter-logging,所以Spring Boot就默认集成了Logback 。2、 配置信息在resource下创建logback-spring.xml文件然后在yml中配置logging.config=classpath:log/logback-spring.xmllogging.path=D:/nmyslog/nmys最后将下面的代码复制到logback-spring.xml里。<?xml version="1.0" encoding="UTF-8"?><!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --><!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true --><!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --><!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> < configuration scan =" true " scanPeriod =" 10 seconds "><!--<include resource="org/springframework/boot/logging/logback/base.xml"/> -->< contextName > Logback For Boss </ contextName ><!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->< property name =" log.path " value =" F:/locakback " /><!-- 定义日志文件 输入位置 -->< property name =" logDir " value =" F:/logbak " /><!-- 日志最大的历史 30天 -->< property name =" maxHistory " value =" 30 " /><!-- 控制台输出日志 -->< appender name =" STDOUT " class =" ch.qos.logback.core.ConsoleAppender ">< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder ></ appender ><!-- ERROR级别日志 -->< appender name =" ERROR "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > ERROR </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\error.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger -%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!-- WARN级别日志 -->< appender name =" WARN "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > WARN </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\warn.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!-- INFO级别日志 -->< appender name =" INFO "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > INFO </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\info.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!-- DEBUG级别日志 -->< appender name =" DEBUG "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > DEBUG </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\debug.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!--文件日志, 按照每天生成日志文件 -->< appender name =" FILE " class =" ch.qos.logback.core.rolling.RollingFileAppender ">< rollingPolicy class =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy "><!--日志文件输出的文件名-->< FileNamePattern > ${logDir}/%d{yyyyMMdd}/boss.%d{yyyy-MM-dd}.log </ FileNamePattern ><!--日志文件保留天数-->< MaxHistory > 30 </ MaxHistory ></ rollingPolicy >< encoder class =" ch.qos.logback.classic.encoder.PatternLayoutEncoder "><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n </ pattern ></ encoder ><!--日志文件最大的大小-->< triggeringPolicy class =" ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy ">< MaxFileSize > 10MB </ MaxFileSize ></ triggeringPolicy ></ appender ><!-- 异步输出 -->< appender name =" dayLogAsyncAppender " class =" ch.qos.logback.classic.AsyncAppender ">< includeCallerData > true </ includeCallerData ><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->< discardingThreshold > 0 </ discardingThreshold ><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->< queueSize > 512 </ queueSize >< appender-ref ref =" FILE "/></ appender ><!--专为 spring 定制-->< logger name =" org.springframework " level =" info "/><!-- show parameters for hibernate sql 专为 Hibernate 定制 -->< logger name =" org.hibernate.type.descriptor.sql.BasicBinder " level =" TRACE " />< logger name =" org.hibernate.type.descriptor.sql.BasicExtractor " level =" DEBUG " />< logger name =" org.hibernate.SQL " level =" DEBUG " />< logger name =" org.hibernate.engine.QueryParameters " level =" DEBUG " />< logger name =" org.hibernate.engine.query.HQLQueryPlan " level =" DEBUG " /><!--myibatis log configure--><!--<logger name="com.apache.ibatis" level="TRACE"/><logger name="java.sql.Connection" level="DEBUG"/><logger name="java.sql.Statement" level="DEBUG"/><logger name="java.sql.PreparedStatement" level="DEBUG"/>--><!-- root级别 DEBUG -->< root level =" INFO "><!-- 控制台输出 -->< appender-ref ref =" STDOUT " /><!-- 文件输出 -->< appender-ref ref =" ERROR " />< appender-ref ref =" INFO " />< appender-ref ref =" WARN " />< appender-ref ref =" DEBUG " /></ root ></ configuration >大工告成!
  • [技术干货] IOC 容器的初始化过程
      最近复习了一遍Spring IOC容器的初始化过程,结合书籍《Spring源码深度解析》总结了一下,IOC容器的初始化过程,大概分为以下三点:1、定位资源:  定位相关的配置文件,扫描相关注解2、加载资源:  将配置信息加载到内存中3、注册:  根据载入的配置信息,初始化对象,并将其装载至容器中POM文件<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springTest</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies> </project>测试代码public class ServiceB { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Object serviceA = context.getBean("serviceA"); System.out.println(serviceA); }xml配置<?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" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <context:component-scan base-package="com.donkeys.spring"/> <bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean> </beans>ClassPathXmlApplicationContext构造方法 public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //根据传入的配置文件名称,调用父类的setConfigLocations方法,解析配置文件路径, setConfigLocations(configLocations); //refresh = true //refresh() 方法会重启整个容器 if (refresh) { refresh(); } }  构造方法中一共做了2件事,首先是设置配置文件的路径,然后对整个容器进行刷新。  这里我们重点关注refresh()方法;进入refresh()方法AbstractApplicationContext类的refresh()方法@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //为刷新前做准备 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //获取IOC容器,这里就是处理资源定位以配置文件加载/注册的方法 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }  这里主要关注**obtainFreshBeanFactory()**方法/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //刷新IOC容器 //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory方法,具体调用实现调用子类的refreshBeanFactory方法 refreshBeanFactory(); //获取一个新的容器 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }  **obtainFreshBeanFactory()**方法总共干了2件事,  重置容器,refreshBeanFactory()方法中会设置相关标志,清除旧的容器,同时为Spring上下文生成一个新的容器,获取一个新的容器AbstractRefreshableApplicationContext的refreshBeanFactory()方法  下面我们进入**refreshBeanFactory()**方法/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. * 该方法会将之前的bean工厂全部关闭,并初始化一个全新的bean 工厂类 用于Spring 上下文的生命周期 * bean工厂就是IOC容器 */ @Override protected final void refreshBeanFactory() throws BeansException { //判断是否之前也有容器 //如果有就销毁掉 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建一个新的工厂 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //读取Bean对象的定义 //这里也是使用的委派设计模式 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }  这里创建了新的容器工厂,同时将新的工厂传入了loadBeanDefinitions()方法中,下面来看一下在loadBeanDefinitions方法中具体做了什么操作。AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法  在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中,调用了loadBeanDefinitions方法,但是这个方法它的一个抽象方法,具体实现应由子类去实现,我们在程序启动时,使用的ClassPathXmlApplicationContext类。根据文章开头的类图可以知道,这里会调用子类AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加载@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //使用默认的beanFactory去创建 XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //设置资源的加载环境 beanDefinitionReader.setEnvironment(this.getEnvironment()); //设置资源读取器 beanDefinitionReader.setResourceLoader(this); //设置实体解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader); //使用初始化完成的读取器,调用loadBeanDefinitions方法 loadBeanDefinitions(beanDefinitionReader); }  总的来说,这里只干了一件事,那就是初始化配置//设置资源读取器 beanDefinitionReader.setResourceLoader(this); //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader);  设置资源读取器,这里设置的资源读取器就是当前这个对象本身  通过类图我们可以发现我们这个类的顶级父类ApplicationContext,继承自DefaultResourceLoader这个类,该类实现了ResourceLoader接口,说明这个类的实例化对象本身是具有资源读取器的功能的  初始化bean对象定义读取器,这里设置xml文件的校验方式  下面我们继续看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //从子类对象中获取到资源定位 Resource[] configResources = getConfigResources(); if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }  先看这一行//这行代码的具体实现是由子类完成的,通过类图可以知道AbstractXmlApplicationContext的子类为ClassPathXmlApplicationContext //这里主要将我们最开始在构造方法中设置好的配置文件进行返回 Resource[] configResources = getConfigResources();  再看这一行//如果返回的配置文件不为空,就将返回的已经封装好的资源文件进行读取, if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); }  至此。资源文件定位过程已经加载完成。后续就是读取和注册。整个IOC容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。
  • [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: Spring jar 包和源代码schema: Spring 配置文件的约束DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。
  • [技术干货] if else 终结者 - 策略模式
    策略模式引入  在软件开发中,我们常常会遇到这样的情况,实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。  譬如商场购物场景中,有些商品按原价卖,商场可能为了促销而推出优惠活动,有些商品打九折,有些打八折,有些则是返现10元等。而优惠活动并不影响结算之外的其他过程,只是在结算的时候需要根据优惠方案结算。  再比如不同的人出去旅游出行的交通方式也不同,经济条件好的会选择高铁飞机,而普通人可能会选择绿皮火车。  富豪老王打算去西藏旅游,老王定了豪华酒店,并且定了机票当天直达。而普通人老张也要去西藏旅游,他打算选择乘坐高铁出行。而学生党的我小汪肯定会选择绿皮火车,主要是为了看路边的风景,而不是因为穷。  下面我们用代码来描述一下上诉场景:public class Travel { private String vehicle;//出行方式 private String name; public String getName() { return name; } public Travel(String name) { this.name = name; } public void setName(String name) { this.name = name; } public void setVehicle(String vehicle) { this.vehicle = vehicle; } public String getVehicle() { return vehicle; } public void TravelTool(){ if(name.equals("小汪")){ setVehicle("绿皮火车"); }else if(name.equals("老张")){ setVehicle("高铁"); }else if(name.equals("老王")){ setVehicle("飞机"); } System.out.println(name+"选择坐"+getVehicle()+"去西藏旅游"); } } public class Test { public static void main(String[] args) { Travel travel1 = new Travel("小汪"); Travel travel2 = new Travel("老王"); Travel travel3 = new Travel("老张"); travel1.TravelTool(); travel2.TravelTool(); travel3.TravelTool(); } }小汪选择坐绿皮火车去西藏旅游 老王选择坐飞机去西藏旅游 老张选择坐高铁去西藏旅游  以上代码虽然完成了我们的需求,但是存在以下问题:  Travel类的TravelTool方法非常庞大,它包含各种人的旅行实现代码,在代码中出现了较长的 if…else… 语句,假如日后小汪发达了也想体验一下做飞机去西藏旅游,那就要去修改TravelTool方法。违反了 “开闭原则”,系统的灵活性和可扩展性较差。  算法的复用性差,如果在另一个系统中需要重用某些算法,只能通过对源代码进行复制粘贴来重用,无法单独重用其中的某个或某些算法。策略模式策略模式的介绍  策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。  这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。策略模式的原理类图角色分析  Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。  Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。  ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。  我们下面用策略模式来改进一下上面旅行的代码例子。抽象策略类 Discountpublic abstract class AbstractTravle { private String vehicle; private String name; public AbstractTravle(String vehicle, String name) { this.vehicle = vehicle; this.name = name; } public String getVehicle() { return vehicle; } public String getName() { return name; } public abstract void TravelTool(); }ConcreteStrategy(具体策略类)public class XiaoWang extends AbstractTravle{ public XiaoWang(String vehicle, String name) { super(vehicle, name); } @Override public void TravelTool() { System.out.println(getName()+"选择坐"+getVehicle()+"去西藏旅游"); } } public class LaoWang extends AbstractTravle{ public LaoWang(String vehicle, String name) { super(vehicle, name); } @Override public void TravelTool() { System.out.println(getName()+"选择坐"+getVehicle()+"去西藏旅游"); } } public class LaoZhang extends AbstractTravle{ public LaoZhang(String vehicle, String name) { super(vehicle, name); } @Override public void TravelTool() { System.out.println(getName()+"选择坐"+getVehicle()+"去西藏旅游"); } }环境类public class Context { private AbstractTravle abstractTravle; public Context(AbstractTravle abstractTravle) { this.abstractTravle = abstractTravle; } public void TravelTool() { System.out.println(abstractTravle.getName()+"选择坐"+abstractTravle.getVehicle()+"去西藏旅游"); } }public class Test { public static void main(String[] args) { Context context1 = new Context(new LaoWang("飞机", "老王")); context1.TravelTool(); Context context2 = new Context(new LaoZang("高铁", "老张")); context2.TravelTool(); Context context3 = new Context(new XiaoWang("绿皮火车", "小汪")); context3.TravelTool(); } }老王选择坐飞机去西藏旅游 老张选择坐高铁去西藏旅游 小汪选择坐绿皮火车去西藏旅游策略模式总结策略模式的主要优点  策略模式提供了对 “开闭原则” 的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。  策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。  策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式而是通过继承,这样算法的使用就和算法本身混在一起,不符合 “单一职责原则”,而且使用继承无法实现算法或行为在程序运行时的动态切换。  使用策略模式可以避免多重条件选择语句。多重条件选择语句是硬编码,不易维护。  策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。策略模式的主要缺点  客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。  策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。  无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。适用场景  一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据 “里氏代换原则” 和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。  一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。  不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。源码分析策略模式的典型应用Java Comparator 中的策略模式  java.util.Comparator 接口是比较器接口,可以通过 Collections.sort(List,Comparator) 和 Arrays.sort(Object[],Comparator) 对集合和数据进行排序,下面为示例程序@Data @AllArgsConstructor public class Student { private Integer id; private String name; @Override public String toString() { return "{id=" + id + ", name='" + name + "'}"; } }// 降序 public class DescSortor implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o2.getId() - o1.getId(); } }// 升序 public class AscSortor implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.getId() - o2.getId(); } }  通过 Arrays.sort() 对数组进行排序public class Test1 { public static void main(String[] args) { Student[] students = { new Student(3, "张三"), new Student(1, "李四"), new Student(4, "王五"), new Student(2, "赵六") }; toString(students, "排序前"); Arrays.sort(students, new AscSortor()); toString(students, "升序后"); Arrays.sort(students, new DescSortor()); toString(students, "降序后"); } public static void toString(Student[] students, String desc){ for (int i = 0; i < students.length; i++) { System.out.print(desc + ": " +students[i].toString() + ", "); } System.out.println(); } }排序前: {id=3, name='张三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='赵六'}, 升序后: {id=1, name='李四'}, 升序后: {id=2, name='赵六'}, 升序后: {id=3, name='张三'}, 升序后: {id=4, name='王五'}, 降序后: {id=4, name='王五'}, 降序后: {id=3, name='张三'}, 降序后: {id=2, name='赵六'}, 降序后: {id=1, name='李四'},   通过 Collections.sort() 对集合List进行排序public class Test2 { public static void main(String[] args) { List<Student> students = Arrays.asList( new Student(3, "张三"), new Student(1, "李四"), new Student(4, "王五"), new Student(2, "赵六") ); toString(students, "排序前"); Collections.sort(students, new AscSortor()); toString(students, "升序后"); Collections.sort(students, new DescSortor()); toString(students, "降序后"); } public static void toString(List<Student> students, String desc) { for (Student student : students) { System.out.print(desc + ": " + student.toString() + ", "); } System.out.println(); } }排序前: {id=3, name='张三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='赵六'}, 升序后: {id=1, name='李四'}, 升序后: {id=2, name='赵六'}, 升序后: {id=3, name='张三'}, 升序后: {id=4, name='王五'}, 降序后: {id=4, name='王五'}, 降序后: {id=3, name='张三'}, 降序后: {id=2, name='赵六'}, 降序后: {id=1, name='李四'},   我们向 Collections.sort() 和 Arrays.sort() 分别传入不同的比较器即可实现不同的排序效果(升序或降序)  这里 Comparator 接口充当了抽象策略角色,两个比较器 DescSortor 和 AscSortor 则充当了具体策略角色,Collections 和 Arrays 则是环境角色Spring Resource 中的策略模式  Spring 把所有能记录信息的载体,如各种类型的文件、二进制流等都称为资源,譬如最常用的Spring配置文件。  在 Sun 所提供的标准 API 里,资源访问通常由 java.NET.URL 和文件 IO 来完成,尤其是当我们需要访问来自网络的资源时,通常会选择 URL 类。  URL 类可以处理一些常规的资源访问问题,但依然不能很好地满足所有底层资源访问的需要,比如,暂时还无法从类加载路径、或相对于 ServletContext 的路径来访问资源,虽然 Java 允许使用特定的 URL 前缀注册新的处理类(例如已有的 http: 前缀的处理类),但是这样做通常比较复杂,而且 URL 接口还缺少一些有用的功能,比如检查所指向的资源是否存在等。  Spring 改进了 Java 资源访问的策略,Spring 为资源访问提供了一个 Resource 接口,该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。public interface Resource extends InputStreamSource { boolean exists(); // 返回 Resource 所指向的资源是否存在 boolean isReadable(); // 资源内容是否可读 boolean isOpen(); // 返回资源文件是否打开 URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; // 返回资源对应的 File 对象 long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String var1) throws IOException; String getFilename(); String getDescription(); // 返回资源的描述信息 }  Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。  Spring 为 Resource 接口提供的部分实现类如下:  UrlResource:访问网络资源的实现类。  ClassPathResource:访问类加载路径里资源的实现类。  FileSystemResource:访问文件系统里资源的实现类。  ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:  InputStreamResource:访问输入流资源的实现类。  ByteArrayResource:访问字节数组资源的实现类。  WritableResource:写资源文件  类图如下:  可以看到 AbstractResource 资源抽象类实现了 Resource 接口,为子类通用的操作提供了具体实现,非通用的操作留给子类实现,所以这里也应用了模板方法模式。(只不过缺少了模板方法)  Resource 不仅可在 Spring 的项目中使用,也可直接作为资源访问的工具类使用。意思是说:即使不使用 Spring 框架,也可以使用 Resource 作为工具类,用来代替 URL。  譬如我们可以使用 UrlResource 访问网络资源。  也可以通过其它协议访问资源,file: 用于访问文件系统;http: 用于通过 HTTP 协议访问资源;ftp: 用于通过 FTP 协议访问资源等public class Test { public static void main(String[] args) throws IOException { UrlResource ur = new UrlResource("http://image.laijianfeng.org/hello.txt"); System.out.println("文件名:" + ur.getFilename()); System.out.println("网络文件URL:" + ur.getURL()); System.out.println("是否存在:" + ur.exists()); System.out.println("是否可读:" + ur.isReadable()); System.out.println("文件长度:" + ur.contentLength()); System.out.println("\n--------文件内容----------\n"); byte[] bytes = new byte[47]; ur.getInputStream().read(bytes); System.out.println(new String(bytes)); } }文件名:hello.txt 网络文件URL:http://image.laijianfeng.org/hello.txt 是否存在:true 是否可读:true 文件长度:47--------文件内容---------- hello world! welcome to http://laijianfeng.orgSpring Bean 实例化中的策略模式  Spring实例化Bean有三种方式:构造器实例化、静态工厂实例化、实例工厂实例化<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="person" class="com.demo.Person"></bean> <bean id="personWithParam" class="com.demo.Person"> <constructor-arg name="name" value="小旋锋"/> </bean> <bean id="personWirhParams" class="com.demo.Person"> <constructor-arg name="name" value="小旋锋"/> <constructor-arg name="age" value="22"/> </bean> </beans>  具体实例化Bean的过程中,Spring中角色分工很明确,创建对象的时候先通过 ConstructorResolver 找到对应的实例化方法和参数,再通过实例化策略   InstantiationStrategy 进行实例化,根据创建对象的三个分支( 工厂方法、有参构造方法、无参构造方法 ), InstantiationStrategy 提供了三个接口方法:public interface InstantiationStrategy { // 默认构造方法 Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) throws BeansException; // 指定构造方法 Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Constructor<?> ctor, Object[] args) throws BeansException; // 指定工厂方法 Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Object factoryBean, Method factoryMethod, Object[] args) throws BeansException; }  InstantiationStrategy 为实例化策略接口,扮演抽象策略角色,有两种具体策略类,分别为 SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy  在 SimpleInstantiationStrategy 中对这三个方法做了简单实现,如果工厂方法实例化直接用反射创建对象,如果是构造方法实例化的则判断是否有MethodOverrides,如果有无 MethodOverrides 也是直接用反射,如果有 MethodOverrides 就需要用 cglib 实例化对象,SimpleInstantiationStrategy 把通过 cglib 实例化的任务交给了它的子类 CglibSubclassingInstantiationStrategy。    
  • [技术干货] Spring Boot 项目开发环境搭建
    方式1:官网配置生成第一步:进入Spring官网https://spring.io/  进来后是这个页面第二步:选中Projects下拉菜单中的Spring Initializr选项  点击进入第三步:填写project信息  选择项目管理工具,语言,Spring Boot版本,项目信息填写等等,傻瓜式地选择填写就完事。  项目需要哪方面的组件依赖,在右侧搜索添加就行。第四步:点击CENERATE  点击后会下载一个项目压缩包,减压到开发工具(我用的是IDEA)的工作目录,然后在工具中导入项目即可。方式2:IDEA中生成  Idea 集成了 https://start.spring.io/,可以在 idea 中选择配置并生成.第一步:新建项目,选择Spring Initlalizr  如下  Next第二步:填写protect信息  这一步跟官网生成时,填写的东西大同小异,按需填写就行  Next第三步:选择需要的组件依赖  Next第四步:填写项目名称,项目地址  Finish  创建完成!    
  • [解决方案] Spring-kafka对接华为FusionInsight Kafka 报没有方法错误
    【功能模块】开发环境集群版本:6.5.1【操作步骤&问题现象】1、参考文档:Spring-kafka对接华为FusionInsight Kafka样例2、按照参考文档操作,测试时报没有方法错误【截图信息】pom文件:运行日志:【日志信息】(可选,上传日志内容或者附件)
  • [eSDK] SMC2.0集成到现有Spring boot web应用中,调用smc2.0的接口文档用哪个呀?
    Java (Spring boot ) 调用smc2.0的接口文档用哪个呀?
  • [技术干货] 详解Http请求中Content-Type讲解以及在Spring MVC中的应用
    详解Http请求中Content-Type讲解以及在Spring MVC中的应用引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值,以及在spring MVC中如何使用它们来映射请求信息。1.  Content-Type  MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。1234类型格式:type/subtype(;parameter)? type 主类型,任意的字符串,如text,如果是*号代表所有;  subtype 子类型,任意的字符串,如html,如果是*号代表所有;  parameter 可选,一些参数,如Accept请求头的q参数, Content-Type的 charset参数。   例如: Content-Type: text/html;charset:utf-8; 常见的媒体格式类型如下:    text/html : HTML格式    text/plain :纯文本格式         text/xml :  XML格式    image/gif :gif图片格式       image/jpeg :jpg图片格式    image/png:png图片格式   以application开头的媒体格式类型:   application/xhtml+xml :XHTML格式   application/xml     : XML数据格式   application/atom+xml  :Atom XML聚合格式      application/json    : JSON数据格式   application/pdf       :pdf格式    application/msword  : Word文档格式   application/octet-stream : 二进制流数据(如常见的文件下载)   application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)   另外一种常见的媒体格式是上传文件之时使用的:    multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式     以上就是我们在日常的开发中,经常会用到的若干content-type的内容格式。2.   Spring MVC中关于关于Content-Type类型信息的使用    首先我们来看看RequestMapping中的Class定义:123456789101112@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented@Mappingpublic @interface RequestMapping {    String[] value() default {};    RequestMethod[] method() default {};    String[] params() default {};    String[] headers() default {};    String[] consumes() default {};    String[] produces() default {}; } value:  指定请求的实际地址, 比如 /action/info之类。method:  指定请求的method类型, GET、POST、PUT、DELETE等consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;produces:    指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回params: 指定request中必须包含某些参数值是,才让该方法处理headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求其中,consumes, produces使用content-typ信息进行过滤信息;headers中可以使用content-type进行过滤和判断。3. 使用示例  3.1 headers1234@RequestMapping(value = "/test", method = RequestMethod.GET, headers="Referer=http://www.ifeng.com/")  public void testHeaders(@PathVariable String ownerId, @PathVariable String petId) {     // implementation omitted  }    这里的Headers里面可以匹配所有Header里面可以出现的信息,不局限在Referer信息。  示例212345678@RequestMapping(value = "/response/ContentType", headers = "Accept=application/json")  public void response2(HttpServletResponse response) throws IOException {    //表示响应的内容区数据的媒体类型为json格式,且编码为utf-8(客户端应该以utf-8解码)    response.setContentType("application/json;charset=utf-8");    //写出响应体内容    String jsonData = "{\"username\":\"zhang\", \"password\":\"123\"}";    response.getWriter().write(jsonData);  }  服务器根据请求头“Accept=application/json”生产json数据。当你有如下Accept头,将遵守如下规则进行应用:①Accept:text/html,application/xml,application/json      将按照如下顺序进行produces的匹配 ①text/html ②application/xml ③application/json②Accept:application/xml;q=0.5,application/json;q=0.9,text/html      将按照如下顺序进行produces的匹配 ①text/html ②application/json ③application/xml      参数为媒体类型的质量因子,越大则优先权越高(从0到1)③Accept:*/*,text/*,text/html      将按照如下顺序进行produces的匹配 ①text/html ②text/* ③*/*即匹配规则为:最明确的优先匹配。3.2 params的示例1234@RequestMapping(value = "/test/{userId}", method = RequestMethod.GET, params="myParam=myValue")  public void findUser(@PathVariable String userId) {     // implementation omitted  }    仅处理请求中包含了名为“myParam”,值为“myValue”的请求,起到了一个过滤的作用。3.3 consumes/produces1234567@Controller @RequestMapping(value = "/users", method = RequestMethod.POST, consumes="application/json", produces="application/json")  @ResponseBodypublic List<User> addUser(@RequestBody User userl) {      // implementation omitted    return List<User> users; }    方法仅处理request Content-Type为“application/json”类型的请求. produces标识==>处理request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json;4. 总结  在本文中,首先介绍了Content-Type主要支持的格式内容,然后基于@RequestMapping标注的内容介绍了主要的使用方法,其中,headers, consumes,produces,都是使用Content-Type中使用的各种媒体格式内容,可以基于这个格式内容来进行访问的控制和过滤。
  • [集成开发] Spring集成访问FusionInsight
    Spring官方没有对接hbase和hive的样例,有对接kafka ES的样例本样例提供 spring 集成hbase client ,hive jdbc client以及carbon jdbc client集成样例,仅供参考,业务开发需要考虑更安全可靠的场景,业务代码需要进行完善1.hive是SQL大部分是使用MapReduce进行批处理,意味着执行一条SQL可能需要很久,而我们无论创建单链接和并发链接,可能都不够用,这时就需要严格控制SQL的并发执行条数,样例中提供了使用druid连接池访问hive,hive连接池不能无限扩大,虽然说metastore连接数最大是600,考虑到不止这一个终端执行SQL,这个值建议在100以下,至于如何控制并发执行的SQL条数小于连接池最大大小以及控制服务端并发执行对hiveserver内存的需求,业务需要将这点考虑进去2.druid连接池对于hadoop支持的不是很友好,使用spark自带jdbc jar包 hive-jdbc-1.2.1.spark_2.3.2 的会影响部分功能使用,也就是说如果hive也使用此jar包同样会受到影响,具体可能的错误如下:此ERROR意思是不支持类似oracle SQL中获取字段的大小,但是此报错不影响SQL执行功能(具体需要各个场景都进行验证一遍),可以考虑使用下面两种方法完善1)对于连接池理解深刻的话可以自己开发一套连接池,这样就可以完善此场景了2)也可以对对druid代码进行完善,修改代码,重新编译,这个error可以完全只输出warn日志3)如果仅仅是集成单独访问hive的场景,可以使用hive-jdbc-3.1版本3.在连接hbase和HDFS过程可能会遇到下列报错1)这个需要添加依赖包hadoop-plugin,如果使用其他代理类可能不会碰到这个问题2)如果碰到下列问题说明需要将hadoop-hdfs-client包添加进入依赖,这也是hadoop2.7到hadoop 3.1的变化,包分离开了
  • [技术干货] IOC 容器的初始化过程
      最近复习了一遍Spring IOC容器的初始化过程,结合书籍《Spring源码深度解析》总结了一下,IOC容器的初始化过程,大概分为以下三点:1、定位资源:  定位相关的配置文件,扫描相关注解2、加载资源:  将配置信息加载到内存中3、注册:  根据载入的配置信息,初始化对象,并将其装载至容器中POM文件<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springTest</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies> </project>测试代码public class ServiceB { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Object serviceA = context.getBean("serviceA"); System.out.println(serviceA); }xml配置<?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" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <context:component-scan base-package="com.donkeys.spring"/> <bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean> </beans>资源定位过程解析  代码中,使用的是ClassPathXmlApplicationContext类去加载Sping的配置文件,所以先给出该类类图ClassPathXmlApplicationContext构造方法 public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //根据传入的配置文件名称,调用父类的setConfigLocations方法,解析配置文件路径, setConfigLocations(configLocations); //refresh = true //refresh() 方法会重启整个容器 if (refresh) { refresh(); } }  构造方法中一共做了2件事,首先是设置配置文件的路径,然后对整个容器进行刷新。  这里我们重点关注refresh()方法;进入refresh()方法AbstractApplicationContext类的refresh()方法@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //为刷新前做准备 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //获取IOC容器,这里就是处理资源定位以配置文件加载/注册的方法 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }  这里主要关注**obtainFreshBeanFactory()**方法/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //刷新IOC容器 //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory方法,具体调用实现调用子类的refreshBeanFactory方法 refreshBeanFactory(); //获取一个新的容器 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }  **obtainFreshBeanFactory()**方法总共干了2件事,  重置容器,refreshBeanFactory()方法中会设置相关标志,清除旧的容器,同时为Spring上下文生成一个新的容器,获取一个新的容器AbstractRefreshableApplicationContext的refreshBeanFactory()方法  下面我们进入**refreshBeanFactory()**方法/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. * 该方法会将之前的bean工厂全部关闭,并初始化一个全新的bean 工厂类 用于Spring 上下文的生命周期 * bean工厂就是IOC容器 */ @Override protected final void refreshBeanFactory() throws BeansException { //判断是否之前也有容器 //如果有就销毁掉 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建一个新的工厂 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //读取Bean对象的定义 //这里也是使用的委派设计模式 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }  这里创建了新的容器工厂,同时将新的工厂传入了loadBeanDefinitions()方法中,下面来看一下在loadBeanDefinitions方法中具体做了什么操作。AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法  在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中,调用了loadBeanDefinitions方法,但是这个方法它的一个抽象方法,具体实现应由子类去实现,我们在程序启动时,使用的ClassPathXmlApplicationContext类。根据文章开头的类图可以知道,这里会调用子类AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加载@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //使用默认的beanFactory去创建 XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //设置资源的加载环境 beanDefinitionReader.setEnvironment(this.getEnvironment()); //设置资源读取器 beanDefinitionReader.setResourceLoader(this); //设置实体解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader); //使用初始化完成的读取器,调用loadBeanDefinitions方法 loadBeanDefinitions(beanDefinitionReader); }  总的来说,这里只干了一件事,那就是初始化配置//设置资源读取器 beanDefinitionReader.setResourceLoader(this); //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader);  设置资源读取器,这里设置的资源读取器就是当前这个对象本身  通过类图我们可以发现我们这个类的顶级父类ApplicationContext,继承自DefaultResourceLoader这个类,该类实现了ResourceLoader接口,说明这个类的实例化对象本身是具有资源读取器的功能的  初始化bean对象定义读取器,这里设置xml文件的校验方式  下面我们继续看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //从子类对象中获取到资源定位 Resource[] configResources = getConfigResources(); if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }  先看这一行//这行代码的具体实现是由子类完成的,通过类图可以知道AbstractXmlApplicationContext的子类为ClassPathXmlApplicationContext //这里主要将我们最开始在构造方法中设置好的配置文件进行返回 Resource[] configResources = getConfigResources();  再看这一行//如果返回的配置文件不为空,就将返回的已经封装好的资源文件进行读取, if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); }  至此。资源文件定位过程已经加载完成。后续就是读取和注册。整个IOC容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。————————————————
  • [问题求助] spring cloud工程,运行后无法连接mysql数据库,但是可以连接rds(mysql)
    【功能模块】cloud的工程,启动初始化jdbc无法连接mysql。【操作步骤&问题现象】1、在鲲鹏ecs本地安装的mysql5.7连接不上。2、通过内网连接另外一台x86上的mysql5.7也连接不上。3,通过测试可以连接上rds(mysql)。4,鲲鹏上安装的mysql与x86上安装mysql都可以通过navicat连接。5,连接池为:hikari【截图信息】【日志信息】日志在附件当中
  • [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: Spring jar 包和源代码schema: Spring 配置文件的约束 DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。 控制反转(IOC)控制反转(Inversion of Control)是指将对象的创建权反转(交给)Spring。使用IOC就需要导入IOC相关的包,也就是上图中核心容器中的几个包:beans,context,core,expression四个包。 实现原理传统方式创建对象:UserDAO userDAO=new UserDAO();进一步面向接口编程,可以多态:UserDAO userDAO=new UserDAOImpl();这种方式的缺点是接口和实现类高耦合,切换底层实现类时,需要修改源代码。程序设计应该满足OCP元祖,在尽量不修改程序源代码的基础上对程序进行扩展。此时,可以使用工厂模式:class BeanFactory{    public static UserDAO getUserDAO(){        return new UserDAOImpl();    }}此种方式虽然在接口和实现类之间没有耦合,但是接口和工厂之间存在耦合。使用工厂+反射+配置文件的方式,实现解耦,这也是 Spring 框架 IOC 的底层实现。//xml配置文件//<bean id="userDAO" class="xxx.UserDAOImpl"></bean>class BeanFactory{    public static Object getBean(String id){        //解析XML        //反射        Class clazz=Class.forName();        return clazz.newInstance();    }}IOC XML 开发在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    //在此配置bean    <bean id="userService" class="x.y.UserServiceImpl">    </bean></beans>调用类:ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();IOC 和 DIDI 指依赖注入,其前提是必须有 IOC 的环境,Spring 管理这个类的时候将类的依赖的属性注入进来。例如,在UserServiceImpl.java中:public class UserServiceImpl implements UserService{private String name;public void setName(String name){this.name=name;}public void save(){System.out.println("save "+name);}}在配置文件中:<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="userService" class="spring.demo1.UserServiceImpl">    <!--配置依赖的属性-->     <property name="name" value="tony"/>    </bean></beans>测试代码:@Testpublic void demo2(){//创建Spring工厂ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();}运行结果:save tony可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。Spring 的工厂类BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:ClassPathXmlApplicationContext: 加载类路径下的配置文件FileSystemXmlApplicationContext: 加载磁盘下的配置文件bean标签配置id: 唯一约束,不能出现特殊字符name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符生命周期:init-method: bean被初始化的时候执行的方法destroy-method: bean被销毁的时候执行的方法作用范围:scope: bean的作用范围,有如下几种,常用的是前两种singleton: 默认使用单例模式创建prototype: 多例request: 在web项目中,spring 创建类后,将其存入到 request 范围中session: 在web项目中,spring 创建类后,将其存入到 session 范围中globalsession: 在web项目中,必须用在 porlet 环境属性注入设置构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。<bean id="car" class="demo.Car">    <constructor-arg name="name" value="bmw">    <constructor-arg name="price" value="123"></bean>set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。<bean id="employee" class="demo.Employee">    <property name="name" value="xiaoming">    <property name="car" ref="car"></bean>P名称空间的属性注入: 首先需要引入p名称空间:<beans xmlns="http://www.springframework.org/schema/beans"    //引入p名称空间    xmlns:p="http://www.springframework.org/schema/p"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>如果是普通属性:<bean id="car" class="demo.Car" p:name="bmv" p:price="123"></bean>如果是引用类型:<bean id="employee" class="demo.Employee" p:name="xiaoming" p:car-ref:"car"></bean>SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)<bean id="car" class="demo.Car">    <property name="name" value="#{'xiaoming'}">    <property name="car" ref="#{car}"></bean>集合类型属性注入:<bean id="car" class="demo.Car">    <property name="namelist">        <list>            <value>qirui</value>            <value>baoma</value>            <value>benchi</value>        </list>    </property></bean>多模块开发配置在加载配置文件的时候,加载多个配置文件在一个配置文件中引入多个配置文件,通过实现IOC 注解开发示例引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)<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/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">         <!-- bean definitions here --></beans>组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。<context:component-scan base-package="demo1">在类上添加注解使用注解设置属性的值属性如果有set方法,将属性注入的注解添加到set方法属性没有set方法,将注解添加到属性上。@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类public class UserDAOImpl implements UserDAO {@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("save");} }注解详解@Component组件注解,用于修饰一个类,将这个类交给 Spring 管理。有三个衍生的注解,功能类似,也用来修饰类。@Controller:修饰 web 层类@Service:修饰 service 层类@Repository:修饰 dao 层类2.属性注入普通属性使用 @Value 来设置属性的值对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入3.其他注解@PostConstruct 相当于 init-method,用于初始化函数的注解@PreDestroy 相当于 destroy-method,用于销毁函数的注解@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")IOC 的 XML 和注解开发比较适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。可以使用 XML 管理 bean,使用注解来进行属性注入AOP开发AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是OOP的延续。AOP 能够对程序进行增强,在不修改源码的情况下,可以进行权限校验,日志记录,性能监控,事务控制等。也就是说功能分为两大类,一类是核心业务功能,一类是辅助增强功能。两类功能彼此独立进行开发。比如登录功能是核心业务功能,日志功能是辅助增强功能,如果有需要,将日志和登录编制在一起。辅助功能就称为切面,这种能选择性的、低耦合的把切面和核心业务功能结合的编程思想称为切面编程。底层实现JDK 动态代理只能对实现了接口的类产生代理。Cglib 动态代理可以对没有实现接口的类产生代理对象,生成的是子类对象。使用 JDK 动态代理:public interface UserDao {public void insert();public void delete();public void update();public void query();}实现类:public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("insert"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } @Override public void query() { System.out.println("query"); } }JDK 代理:public class JDKProxy implements InvocationHandler{private UserDao userDao;public JDKProxy(UserDao userDao){this.userDao=userDao;}public UserDao createProxy(){UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this);return userDaoProxy;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("update".equals(method.getName())){System.out.println("权限校验");return method.invoke(userDao, args);}return method.invoke(userDao, args);}}通过动态代理增强了 update 函数。 测试类:public class Demo1 {@Testpublic void demo1(){UserDao userDao=new UserDaoImpl();UserDao proxy=new JDKProxy(userDao).createProxy();proxy.insert();proxy.delete();proxy.update();proxy.query();}}运行结果为:insertdelete权限校验updatequeryCglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。与上边JDK代理不同,Cglib的使用方式如下:public class CglibProxy implements MethodInterceptor{//传入增强的对象private UserDao customerDao;public CglibProxy(UserDao userDao){this.userDao=userDao;}public UserDao createProxy(){Enhancer enhancer=new Enhancer();enhancer.setSuperclass(userDao.getClass());enhancer.setCallback(this);UserDao proxy=(UserDao)enhancer.create();return proxy;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {if("save".equals(method.getName())){System.out.println("enhance function");return methodProxy.invokeSuper(proxy, args);}return methodProxy.invokeSuper(proxy, args);}}如果实现了接口的类,底层采用JDK代理。如果不是实现了接口的类,底层采用 Cglib代理。IOC与传统方式的比较获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。Spring 的 AOP 开发(AspectJ 的 XML 方式)AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。相关术语Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。Introduction: 引介,类层面的增强。Target: 目标,被增强的对象(类)。Weaving: 织入,将 advice 应用到 target 的过程。Proxy: 代理对象,被增强的对象。Aspect: 切面,多个通知和多个切入点的组合。使用方法引入相关包引入配置文件<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --></beans>编写目标类并配置:public class ProductDaoImpl implements ProductDao {@Overridepublic void save() {System.out.println("save");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void find() {System.out.println("find");}@Overridepublic void delete() {System.out.println("delete");}}<bean id="productDao" class="demo1.ProductDaoImpl"></bean>编写切面类,假设用于权限验证并配置public class MyAspectXML {public void checkPri(){System.out.println("check auth");}}<bean id="myAspect" class="demo1.MyAspectXML"></bean>通过AOP配置完成对目标类的增强<aop:config><aop:pointcut expression="execution(* demo1.ProductDaoImpl.save(..))" id="pointcut1"/><aop:aspect ref="myAspect"><aop:before method="chechPri" pointcut-ref="pointcut1"/></aop:aspect> </aop:config> 通知类型前置通知:在目标方法执行前操作,可以获得切入点信息<aop:before method="chechPri" pointcut-ref="pointcut1"/>public void checkPri(JoinPoint joinPoint){System.out.println("check auth "+joinPoint);}后置通知:在目标方法执行后操作,可以获得方法返回值<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>public void writeLog(Object result){    System.out.println("writeLog "+result);}环绕通知:在目标方法执行前和后操作,可以阻止目标方法执<aop:around method="around" pointcut-ref="pointcut3"/>public Object around(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("before");Object result=joinPoint.proceed();System.out.println("after");return result;}异常抛出通知:程序出现异常时操作<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>public void afterThrowing(Throwable ex){System.out.println("exception "+ex.getMessage());}最终通知:相当于finally块,无论代码是否有异常,都会执行<aop:after method="finallyFunc" pointcut-ref="pointcut4"/>public void finallyFunc(){System.out.println("finally");}引介通知:不常用Spring 切入点表达式基于 execution 函数完成语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)其中任意字段可以使用*代替表示任意值Spring 的 AOP 基于 AspectJ 注解开发开发步骤引入jar包设置配置文件:<?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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"></beans>编写配置目标类<bean id="orderDao" class="demo1.OrderDao"></bean>public class OrderDao {public void save(){System.out.println("save order");}public void update(){System.out.println("update order");}public void delete(){System.out.println("delete order");}public void find(){System.out.println("find order");}}开启aop注解自动代理<aop:aspectj-autoproxy/>编写切面类并配置@Aspectpublic class MyAspectAnno {@Before(value="execution(* demo1.OrderDao.save(..))")public void before(){System.out.println("before");}}<bean id="myAspect" class="demo1.MyAspectAnno">注解通知类型@Before: 前置通知@AfterReturning: 后置通知@AfterReturning(value="execution(* demo1.OrderDao.save(..))",returning="result")public void after(Object result){System.out.println("after "+result);}@Around:环绕通知@Around(value="execution(* demo1.OrderDao.save(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("before");Object obj=joinPoint.proceed();System.out.println("after");return obj;}@AfterThrowing: 抛出异常@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")public void afterThrowing(Throwable e){System.out.println("exception:"+e.getMessage();}@After: 最终通知@After(value="execution(* demo1.OrderDao.save(..))")public void after(){System.out.println("finally");}@PointCut:切入点注解@PointCut(value="execution(* demo1.OrderDao.save(..))")private void pointcut1(){}此时,在上述通知的注解中,value可以替换为该函数名,例如:@After(value="MyAspect.pointcut1()")public void after(){System.out.println("finally");}这个注解的好处是,只需要维护切入点即可,不用在修改时修改每个注解。Spring 的 JDBC 模板Spring 对持久层也提供了解决方案,也就是 ORM 模块和 JDBC 的模板。针对 JDBC ,提供了 org.springframework.jdbc.core.JdbcTemplate 作为模板类。使用 JDBC 模板引入jar包,数据库驱动,Spring 的 jdbc 相关包。基本使用:public void demo1(){    //创建连接池DriverManagerDataSource dataSource=new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///spring4");dataSource.setUsername("root");dataSource.setPassword("123456");//创建JDBC模板 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);jdbcTemplate.update("insert into account values (null,?,?)", "xiaoming",1000d);}将连接池和模板交给 Spring 管理配置文件:<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource;"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql:///spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate;"> <property name="dataSource" ref="dataSource"></property></bean>测试文件: @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class JdbcDemo2 {@Resource(name="jdbcTemplate")private JdbcTemplate jdbcTemplate;@Testpublic void demo2(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);}}使用开源数据库连接池使用 DBCP 的配置:<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://192.168.66.128/spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property>使用 C3P0 的配置:<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://192.168.66.128/spring4"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean>引入外部属性文件首先建立外部属性文件:jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://192.168.66.128/spring4jdbc.username=rootjdbc.password=123456然后对属性文件进行配置:<context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driverClass}"></property><property name="jdbcUrl" value="${jdbc.url}"></property><property name="user" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean>CRUD操作insert, update, delete 语句都借助模板的 update 方法进行操作。public void demo(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaoda",1000d);jdbcTemplate.update("update account set name=?,money=? where id=?", "xiaoda",1000d,2);jdbcTemplate.update("delete from account where id=?", 6);}查询操作:public void demo3(){String name=jdbcTemplate.queryForObject("select name from account where id=?",String.class,5);long count=jdbcTemplate.queryForObject("select count(*) from account",Long.class);}将返回的结果封装成为类:public void demo4(){Account account=jdbcTemplate.queryForObject("select * from account where id=?", new MyRowMapper(),5);}其中:class MyRowMapper implements RowMapper<Account>{@Overridepublic Account mapRow(ResultSet rs, int rowNum) throws SQLException {Account account=new Account();account.setId(rs.getInt("id"));account.setName(rs.getString("name"));account.setMoney(rs.getDouble("money"));return account;}}Spring的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。具有四个特性:原子性:事务不可分一致性:事务执行前后数据完整性保持一致隔离性:一个事务的执行不应该受到其他事务干扰持久性:一旦事务结束,数据就持久化到数据库如果不考虑隔离性会引发安全性问题:读问题:脏读:一个事务读到另一个事务未提交的数据不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致写问题:丢失更新解决读问题:设置事务隔离级别Read uncommitted: 未提交读,无法解决任何读问题Read committed: 已提交读,解决脏读问题Repeatable read: 重复读,解决脏读和不可重复读问题Serializable:序列化,解决所有读问题事务管理APIPlatformTransactionManager: 平台事务管理器这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。TransactionDefinition: 事务定义信息用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等TransactionStatus: 事务的状态用于记录在事务管理过程中,事务的状态的对象。上述API的关系: Spring 在进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务管理,在事务管理过程当中,产生各种此状态,将这些状态信息记录到事务状态的对象当中。事务的传播行为事务的传播行为主要解决业务层(Service)方法相互调用的问题,也就是不同的业务中存在不同的事务时,如何操作。Spring 中提供了7种事务的传播行为,分为三类:保证多个操作在同一个事务中PROPAGATION_REQUIRED: B方法调用A方法,如果A中有事务,使用A中的事务并将B中的操作包含到该事务中;否则新建一个事务,将A和B中的操作包含进来。(默认)PROPAGATION_SUPPORTS:如果A中有事务,使用A的事务;否则不使用事务PROPAGATION_MANDATORY:如果A中有事务,使用A的事务;否则抛出异常保证多个操作不在同一个事务中PROPAGATION_REQUIRES_NEW:如果A中有事务,将其挂起,创建新事务,只包含自身操作。否则,新建一个事务,只包含自身操作。PROPAGATION_NOT_SUPPORTED:如果A中有事务,挂起,不使用事务。PROPAGATION_NEVER:如果A中有事务,抛出异常,也即不能用事务运行。嵌套事务PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。实例以转账为例,业务层的DAO层类如下:public interface AccountDao {public void outMoney(String from,Double money);public void inMoney(String to,Double money);}public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{@Overridepublic void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);}@Overridepublic void inMoney(String to, Double money) {this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);}}public interface AccountService {public void transfer(String from,String to,Double money);}public class AccountServiceImpl implements AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}@Overridepublic void transfer(String from, String to, Double money) {accountDao.outMoney(from, money);accountDao.inMoney(to, money);}}在xml中进行类的配置:<bean id="accountService" class="tx.demo.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean><bean id="accountDao" class="tx.demo.AccountDaoImpl"><property name="dataSource" ref="dataSource"/></bean> 
  • [技术干货] Spring 的基本配置
    Spring中set方法的注入User实体@Data//lombok提供的有参构造@AllArgsConstructorlombok提供的无参构造@NoArgsConstructorpublic class User {    private int id;    private String name;    private int age;    private String sex;    private String birthday;}    beanFactory.xml<bean id="user" class="edu.xalead.User">        <property name="id" value="1806"/>        <property name="name">            <value>张三</value>        </property>        <property name="sex" value="男"/>        <property name="age" value="18"/>        <property name="birthday" value="2000-1-1"/>    </bean>