spring基本知识概览

1.历史及目标

Rod Johson在2002年《Expert one to one J2EE design and development》,2004年他又推出了一部堪称经典的力作《Expertone-to-one J2EE Development without EJB》,致力于挑战javaEE及EJB的笨重臃肿,将困难的开发任务进行简化,spring不断发展,新增了很多新特性,比如支持REST风格的springMVC,增强安全性的spring security,缓存,消息的支持,spring Boot,spring Cloud等

2.spring的核心

依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)

3.spring简化开发的四个策略

  1. 基于pojo的轻量级和最小侵入性编程(不强制实现spring接口或继承spring类)
  2. 通过DI和面向接口实现低耦合
  3. 基于切面和惯例进行声明式编程
  4. 通过切面和模板(Template)减少样板式代码

tips:

  • 对象通过接口表明依赖关系,就能在对象本身不知情的情况下,用不同实现进行替换,只有spring通过它的配置,能了解这些组成部分是如何装配(wiring)起来的
  • 如果由一个对象自己决定做具体实现,那么会导致紧耦合和难以扩展,不如让依赖的对象自己注入进来,由它自己决定要做什么
  • spring通过应用上下文(Application Context)装载bean的定义并组装,不同的上下文实现的区别在于如何加载配置
  • aop允许你把遍布应用各处的功能分离出来形成可重用的组件,如日志、事务、安全
  • Q:我一个接口只有一般只有一种实现,为什么要使用接口?即使也多个实现也不需要进行实现的替换,为什么还要使用spring的DI呢?
    A:之所以使用接口正是“面向接口编程”这一关注点,对象之间只需要知道规范,不必关心实现;使用spring的DI不仅可以进行解耦,并且它利用容器的单例管理帮助你实现了单例,减少了很多创建销毁对象的性能损耗;最后,使用注入,可以更好的使用AOP,用代理类替代真正的实现类。

4.spring容器(container)

  • spring应用中,对象由spring容器创建和装配,并存在容器中
  • spring有两种类型的容器实现:bean Factory和ApplicationContext,bean工厂往往太低级,应用上下文更受欢迎
    • AnnotationConfigApplicationContext--java配置类加载
    • AnnotationConfigWebApplicationContext--java配置类加载web应用上下文,
    • ClasspathXmlApplicationContext--类路径下的xml加载,
    • FileSystemXmlapplicationcontext--文件系统下的xml加载,
    • XmlWebApplicationContext--web应用下的xml加载

5.spring模块

6.spring boot

以spring的视角,致力于简化spring本身,大量依赖自动配置技术,消除大部分甚至全部spring配置

7.装配bean的三种方式:

  • xml显式配置(维护已有旧配置时使用,最次)
  • java显式配置(配置不是自己维护的源码,而你需要为这些代码配置bean,次之)
  • 隐式的bean发现机制和自动装配(最优)

8.自动化装配bean:

  1. 组件扫描(component scanning):spring自动发现上下文中创建的bean
  2. 自动装配(autowiring):spring自动满足bean之间的依赖

使用方法:

  1. 组件上添加Component注解

    @Component
    public class BraveKnight implements Knight {
  2. 在JavaConfig类(通常会将它放入单独的包中)上启用组件扫描:
    @Configuration
    @ComponentScan
    public class Config {
    
    }
  3. 使用junit4测试
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = Config.class)
    public class Test {
        
        @Autowired
        private Knight knight;
    
        @org.junit.Test
        public void test1(){
            assertNotNull(knight);
        }
    
    }

tips:

  • bean的默认ID为类名的第一个字母变小写,使用Autowired注入成员变量时,如果接口只有一个实现,那么变量名称可以随便取都可以找到,当有两个及以上实现时,变量名称需取bean的ID才能找到
  • 设置组件扫描包:
    @ComponentScan(“com.xx”)

9.通过java代码装配bean

比起xml装配更强大、类型安全且对重构友好

@Bean
    public Knight knight(){
        return new BraveKnight() ;
    }

bean的ID与方法名相同,依赖可以使用构造器注入(强依赖)和setter()方法(可选性依赖)注入

 10.混合配置

@Import(ss.class)   @ImportResource("xx.xml")

<bean class="ssConfig" />  <import resource="xx.xml">

11.多环境装配

@Profile

12.条件化的bean

@Conditional

13.表示首选bean

@Primary

14.bean的作用域

单例(Singleton):默认,在整个应用只创建bean的一个实例,对象无状态时使用

原型(Prototype):每次注入或者通过上下文获取的时候,都会创建一个新的bean实例,bean可变,有状态时使用

@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)

会话(Session):在Web应用中,每个会话创建一个bean

请求(Request):在Web应用中,每个请求创建一个bean

@Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode=ScopedProxyMode.INTERFACES)

tips:

  • 在使用Session和Request作用域时,需设置proxyMode属性,考虑将Session或Request属性的bean注入在单例bean时,若系统启动创建单例bean时,此时会话作用域的bean还未存在;另外,系统中会存在多个Session或Request作用域的实例,我们希望单例bean中注入的恰好是当前Session或Request所对应的bean。因此,设置代理后,系统会在被依赖的地方注入一个代理,当代理被调用时,代理会将调用委托给真正的bean
  • bean没有接口时,需使用CGlib生成基于类的代理
    proxyMode=ScopedProxyMode.TARGET_CLASS

15.AOP术语

  • 通知(Advice):描述切面要完成的工作及什么时候执行
    前置通知(Before),后置通知(After),返回通知(After-returning),异常通知(After-throwing),环绕通知(Around)
  • 连接点(Join point):应用执行中能够插入切面的一个点
  • 切点(Pointcut):匹配通知要织入的一个或多个连接点
  • 切面(Aspect):通知和切点的结合(要做什么,何时,何处)
  • 引入(Introduction):引入允许我们添加新方法或属性
  • 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程

16.spring的AOP

  • 织入:代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean,当代理拦截到方法调用时,在调用目标bean方法之前或之后,会执行切面逻辑
  • 引入:当引入接口的方法被调用时,代理会将调用委托给实现了新接口的某个其他对象。实际上,一个bean的实现被拆分到了多个类中
  • spring在运行时用到被代理的bean时,才会创建代理对象
  • spring只支持方法连接点,AspectJ和JBoss,除了方法切点,还支持字段和构造器接入点

织入示例:

  1. 定义切面
    @org.aspectj.lang.annotation.Aspect
    @Component
    public class Aspect {
        
        @Pointcut("execution(** springDemo.Knight.embarkOnQuest(..))")
        public void pointCut(){}
        
        @Before("pointCut()")
        public void before(){
            System.out.println("before");
        }
        
        @Around("pointCut()")
        public void around(ProceedingJoinPoint jp){
            System.out.println("before");
            try {
                jp.proceed();
            } catch (Throwable e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.err.println("end");
        }
        
    }
  2. 在javaConfig中启用自动代理功能
    @Configuration
    @ComponentScan
    @EnableAspectJAutoProxy
    public class Config {
    
    }

    xml方式:<aop:aspjectj-autoproxy />

  3. 在切点前后即可看到通知生效

引入示例:

  1. 创建引入的接口及一个具体实现:
    public interface ImportMethod {
        public void add();
    }
    public class ImportMethodImpl implements ImportMethod {
        @Override
        public void add() {
            System.out.println("add");
        }
    }
  2. 切面定义:
    @org.aspectj.lang.annotation.Aspect
    @Component
    public class Aspect {
        
     @DeclareParents(value="com.alibaba.springDemo.Knight+",defaultImpl=ImportMethodImpl.class)
        public ImportMethod importMethod;
    
    }

    其中value指明了那种类型的bean要引入该接口,“+”表示所有子类型,
    defaultImpl指定了为引入功能提供实现的具体类,
    @DeclareParents注解了要引入的接口

  3. 使用
    ImportMethod im = (ImportMethod)knight;//将bean转为新的接口类型
    im.add();//执行引入方法

     

 

posted on 2017-03-29 19:02  伪善者ql  阅读(292)  评论(0编辑  收藏  举报

导航