• [其他] Spring三大核心思想之AOP(面向切面编程)
    一、什么是AOP(面向切面编程)?AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。AOP (面向切面编程)是 OOP(面向对象) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程 的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。二、AOP 的作用及其优势作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强优势:减少重复代码,提高开发效率,并且便于维护三、AOP 的底层实现实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现 的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。动态代理的作用:    1)在目标类源代码不改变的情况下,增加功能。    2)减少代码的重复    3)专注业务逻辑代码    4)解耦合,让你的业务功能和日志,事务非业务功能分离。代理设计模式详细笔记 :代理模式详细笔记,想学好Spring的AOP思想先理解代理模式java设计模式之代理设计模式笔记详细内容:代理模式是结构型模式其中的一种现在开发中存在的问题什么是代理模式,为什么需要使用代理模式静态代理及实现什么是动态代理JDK 动态代理和cglib动态代理的使用及区别三种动态代理的对比及优缺点代理模式的使用场景四、AOP 相关概念Spring 的 AOP 实现底层就是对动态代理的代码进行了封装 ,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:1、切面(Aspect)一个切面就是一个代理对象= 要为那些类生成代理+要添加的额外功能是那些2、切入点(pointcut):将来要为那些类生成代理对象3、通知/ 增强(advice):就是要添加的额外功能生活案例:给面包之间涂果酱注意:切面(Aspect)=切入点(pointcut)+通知点(advice,额外功能)4、AOP中的(面向切面编程)通知前置通知:目标方法之前的额外功能 MethodBeforeAdvice环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式 MethodInterceptor(Interceptor [ɪntəˈsɛptə]拦截器)后置通知:目标方法之后的额外功能 AfterReturningAdvice(returning[rɪˈtɜːrnɪŋ])异常通知:执行异常的额外功能 ThrowsAdvice最终通知:一定执行的额外功能5、Target(目标对象):代理的目标对象6、代理(Proxy[ˈprɑːksi]):一个类被AOP注入增强后,就产生一个结果代理类7、连接点(joinPoint):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法(额外功能),因为spring只支持方法类型的连接点五、切面编程步骤1、五种通知类AOP 切面=切入点+通知前置通知:MethodBeforeAdvice后置通知:AfterReturnAdvice环绕通知:MethodInterceptor异常通知:ThrowsAdvice(throw [θroʊz])最终通知通知的配置语法:<aop : 通知类型 method=“切面类中方法名” pointcut=“切点表达式"> </aop:通知类型>1切点表达式的抽取当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。<aop:config>    <!--引用myAspect的Bean为切面对象-->    <aop:aspect ref="myAspect">        <aop:pointcut id="myPointcut" expression="execution(* com.tjcu.aop.*.*(..))"/>        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>    </aop:aspect></aop:config>aop织入的配置<aop:config>    <aop:aspect ref=“切面类”>        <aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>    </aop:aspect></aop:config>切点表达式的写法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))12、AOP的开发步骤1、开发目标类2、开发通知类,确定额外功能3、管理通知类4、配置切入点 确定要为那些类添加额外功能5、将目标类和切面类的对象创建权交给 spring6、组装切面 切入点+通知(额外功能)3、AOP编程所需要的依赖引入依赖   spring-aop   spring-expression   spring-aspects六、AOP实现前置通知案例1、目标接口类public interface CityService {    public void login();    public  void add(String name);}2、目标实现类(核心功能)/** * @author 王恒杰 * @Description:目标对象 */public class CityServiceImpl implements CityService{    @Override    public void login() {        //前置通知:System.out.println("嘻嘻哈哈");        //执行核心的业务逻辑 调用Dao        System.out.println("登录调用Dao");    }    @Override    public void add(String name) {        //前置通知:System.out.println("嘻嘻哈哈");        //执行核心的业务逻辑 调用Dao        System.out.println("添加调用Dao");    }}3、前置通知(额外功能)动态代理代码/** * @author 王恒杰 * @Description: 通知类。额外功能 */public class MyBeforeAdvice implements MethodBeforeAdvice {    /**     * 额外功能书写的方法     * 参数1:代理对象当前调用的方法     * 参数2:当前代理对象调用的方法的参数     * 参数3:目标对象(被代理的对象)     * @param method     * @param objects     * @param o     * @throws Throwable     */    @Override    public void before(Method method, Object[] objects, Object o) throws Throwable {     System.out.println("嘻嘻哈哈");    }}4、将目标类和切面类的对象创建权交给 spring<!--管理目标对象-->    <bean class="before.CityServiceImpl" id="cityService"></bean>    <!--管理通知类 动态代理实现AOP-->    <bean id="myBeforeAdvice" class="before.MyBeforeAdvice"></bean>5、在 spring.xml 中配置织入关系(前置功能)<!--组装切面-->    <aop:config>    <!--配置切入点    id:切入点的唯一标识    expression:切入点表达式,为那些类添加额外功能     execution() 切入点表达式的一种 精确到要添加到额外功能的方法     *:通配     空格:     before.CityServiceImpl.*:所有方法     (..):所有参数    -->        <aop:pointcut id="pc1" expression="execution(* before.CityServiceImpl.*(..))"/>    <!--组装切面=切入点+通知      advice-ref:通知的id      pointcut-ref:切入点的id    -->        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc1"></aop:advisor>    </aop:config>6、测试代码   @Test    public void testMethodBeforeAdvice() {        //启动工厂        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("before/spring.xml");        //获取组件 目标对象就是代理对象        CityService cityService = (CityService) ctx.getBean("cityService");                //目标对象就是代理对象  class com.sun.proxy.$Proxy4        System.out.println(cityService.getClass());                    //调用方法,通过代理类调用目标类        cityService.add("123");    }注意: 获取组件时,目标对象就是代理对象七、Spring中的环绕通知案例1、dao层接口public interface StudentDao {    /**     * 登录     * @param name     */    public void login(String name);    /**     * 分页     * @param name     * @return     */    public String pageShow(String name);}2、dao层实现类public class StudentDaoImpl implements StudentDao{    @Override    public void login(String name) {        //循环10000次        for (int i = 0; i < 10000; i++) {        }        System.out.println("数据库实现登录");    }    @Override    public String pageShow(String name) {        //循环10000次        for (int i = 0; i < 10000; i++) {        }        System.out.println("数据库实现分页");        return name;    }}3、目标接口类public interface StudentService {    /**     * 登录     * @param name     */    public void login(String name);    /**     * 分页     * @param name     * @return     */    public String pageShow(String name);}4、目标实现类public class StudentServiceImpl implements StudentService{    private StudentDao studentDao;    public void setStudentDao(StudentDao studentDao) {        this.studentDao = studentDao;    }    @Override    public void login(String name) {        System.out.println("登录日志");      studentDao.login(name);    }    @Override    public String pageShow(String name) {        System.out.println("分页日志");        String s = studentDao.pageShow(name);        return s;    }}5、环绕通知核心方法: Object proceed = methodInvocation.proceed(); 放行public class StudentAroundAdvice implements MethodInterceptor {    /**     * 参数 内部封装者当前的代理对象 方法的参数,执行方法等     *     * @param methodInvocation     * @return     * @throws Throwable     */    @Override    public Object invoke(MethodInvocation methodInvocation) throws Throwable {        //1.控制事务        System.out.println("控制事务");        //method Invocation 方法调用        System.out.println("当前调用方法的名字" + methodInvocation.getMethod().getName());        //arguments参数 methodInvocation:方法调用        System.out.println("当前的参数为:" + methodInvocation.getArguments()[0]);        System.out.println("--------------");        //记录当前的时间 单位毫秒        long begin = System.currentTimeMillis();        System.out.println("调用查询的数据库");        //放行,执行目标方法 proceed:继续做proceed        Object proceed = methodInvocation.proceed();        //记录结束的时间 单位毫秒        long end = System.currentTimeMillis();        System.out.println("dao执行所用时间" + (end - begin));        return proceed;    }}6、将目标类和切面类的对象创建权交给 spring  <!--管理dao组件-->    <bean id="studentDao" class="com.tjcu.dao.StudentDaoImpl"></bean>    <!--管理Service组件/目标对象-->    <bean id="studentService" class="com.tjcu.service.StudentServiceImpl">        <!--注入值-->        <property name="studentDao" ref="studentDao"></property>    </bean>    <!--管理通知组件-->    <bean id="studentAroundAdvice" class="com.tjcu.advice.StudentAroundAdvice"></bean>7、在 applicationContext.xml 中配置织入关系,aop相关配置 <!--aop相关配置  切面=切点+环绕通知-->    <aop:config>        <!--切入点 execution:[eksɪˈkjuːʃn]执行-->        <aop:pointcut id="pointcut" expression="execution(* com.tjcu.service.StudentServiceImpl.*(..))"/>        <!--组装切面  advisor[ [ædˈvaɪzər]]:顾问  advice-ref:通知  pointcut-ref:切入点-->        <aop:advisor advice-ref="studentAroundAdvice" pointcut-ref="pointcut"></aop:advisor>    </aop:config>8、测试代码  @Test    public void AroundAdviceTest() {        //启动工厂        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/tjcu/Spring/ApplicationContext.xml");        //获取组件 目标对象就是代理对象        StudentService studentService = (StudentService) context.getBean("studentService");        //调用方法,通过代理类调用目标类        studentService.pageShow("通过代理类调用调用目标类");    }
  • [技术干货] 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
    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容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。
  • [技术干货] 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 的基本配置
    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>
  • [热门活动] 【云享读书会第九期】读书笔记征集《深入浅出Spring Security》
    开始活动前,请先报名本期读书会https://developer.huaweicloud.com/activity/reading_9.html--------------------------------------------------------------开发者,你好哟~华为云 · 云享读书会 第9期 它来了 !本期领读书籍为《深入浅出Spring Security》由领读书籍原作者、华为云MVP、华为云云享专家江南一点雨老师,带你精读这本安全架构领域的必读书籍!经过江南一点雨老师的视频领读,如果你有相关知识收获,欢迎在此帖留下你的读书笔记~01. 征集时间2021.03.29 - 2021.04.1102. 读书笔记要求1. 每篇读书笔记字数要求≥300字;2. 内容要求与每日领读视频、领读书籍或是其他安全技术领域相关;3. 内容原创不可抄袭;4. 回帖时请务必留下你的微信昵称和华为云账号。03. 注意事项1. 读书笔记提交后,小助手会在3个工作日内按续完成审核,并增加活动积分100活动积分/篇;2. 本次活动通过提交读书笔记,可获得的积分上限为500分;3. 请务必按照上述要求提交内容,以免影响积分增加;4. 若积分值相同则以完成学习任务的时间先后排序,其中任务完成时间的判定优先级为:读书笔记>自测题>专家Q&A>其他;5. 其他积分获取方式请查看活动社群公告;6. 其他未说明事项请参照 云享读书会《深入浅出Spring Security》04. 最佳读书笔记奖励针对活动时间内提交的有效读书笔记,领读专家将根据内容质量和完成篇数综合评选3位最佳读书笔记获奖者,并奖励提交人 荣耀猎人游戏路由 一个05. 活动排行奖励最终积分排名1~5名最终积分排名6~20名06. 关于云享读书会每期云享读书会活动,会选取一本技术相关的畅销书籍,由原作者/行业专家提炼书籍精华,在读书会的专属微信社群中,每日输出精华知识的领读视频,帮助大家快速积累专业知识。活动期间会设置每日自测题、结业实践任务、提交读书笔记三种积分获取任务,并根据活动结束后的积分排行发放活动奖励。07. 加入学习社群然后添加小助手微信,备注“云享读书会”加入学习社群。
总条数:189 到第 页
上滑加载中