Spring

[TOC]

Spring模块

Spring_第1张图片
untitledq.png
  1. Core Container: 核心容器 spring-context:包含DI, IOC, springEl表达式
  2. Test: 加载Spring 上下文的Spring测试
  3. 数据访问与集成
  4. web 与远程调用
  5. AOP 提供对AOP的支持

核心技术

DI 依赖注入 : IOC容器(控制反转),利用依赖关系注入的方式,实现对象之间的解耦。

AOP 面向切面编程:把应用各处可重用的功能分离出来形成重用组件。

在运行时,动态地将代码切入到类的指定方法、指定位置上

bean容器

  • bean 工厂

  • 应用上下文(基于bean工厂,提供应用框架级别的应用)

    ApplicationContext 是BeanFactory 子类,加强了很多功能。如果使用简单的BeanFactory,大量的功能将失效,比如:transactions 和AOP . 避免直接用BeanFactory

Spring_第2张图片
untitled.png

应用上下文

  • AnnotationConfigApplicationContext

  • AnnotationConfigWebApplicationContext

  • ClassPathXmlApplicationContext

  • FileSystemXmlApplicatonContext

  • XmlWebApplicationContext

    应用上下文通过getBean()获取bean

bean生命周期

Spring_第3张图片
untitled.jpg

回调机制

三种方式实现:

  • 实现InitializingBean 和实现DisposableBean 回调接口
  • 自定义init()和destroy()方法 并XML 面配置 Init-method=”” ; Destroy-method=””
  • @PostConstruct and @PreDestroy 注解。

容器扩展点

实现BeanPostProcessor接口

在Spring容器完成实例化、配置、初始化bean之前之后执行自定义逻辑。
可以配置多个BeanPostProcessor实例,可以设置BeanPostProcessors的order属性来控制其执行次序。
points:

  1. 接口中的两个方法都要将传入的bean返回,而不能返回null
  2. 不要将BeanPostProcessor标记为延迟初始化,因为如果这样做,Spring容器将不会注册它们.
  3. BeanPostProcessors容器级别的。当使用容器继承时BeanPostProcessors不会继承。

注册方式:

  • xml:
  • 注解:@Component 修饰自定义的BeanPostProcessor类。并加到context里面。(声明时或者@ComponentScan)

使用注解的方式因为@Configuration类本身也会注册为一个bean定义,还有一些系统的bean 所以不好控制有效范围, 通过xml 方式容易控制。
e.g. @Required 就是用RequiredAnnotationBeanPostProcessor 实现的。

BeanFactoryPostProcessor

此接口的语法和BeanPostProcessor类似,用于读取配置元数据并且在容器实例化bean之前修改bean配置。这个也是在自己容器内有效,也不会继承。

延迟初始化

ApplicationContext的各种实现默认的初始化处理过程,都是尽早的创建、配置所有的单例bean。lazy-initialized bean告诉Ioc容器,只有在第一次请求的时候采取初始化,而不是在启动容器时初始化。
通过设置元素的default-lazy-init属性,可以设置容器级别的延迟加载。
普通bean 需要的时候初始化。

装配bean

定义bean

  • xml 显式定义

  • 注解显式定义

    @Configuration @Bean @Component @Repository @Service @Controller +组件扫描ComponentScan

@Configuration
@ComponentScan("com.spring.baseIOC")
public class ConfigWithAnnotationTest {

    @Bean   //修饰方法
    @Qualifier("bean1") //如果全局只有一个String 需要被注入,那这里可以省略Qualifier
    String method(@Qualifier("bean2") String a){
        return new String(a);
    }

    @Scope("prototype")
    @Bean(name = "bean2")
    @Qualifier("bean2_qual")
    String method2(){
        return new String("cvszz22");
    }

    @Autowired          //声明这个field 会被自动注入
    @Qualifier("SomeBeanAnnotation")
    private SomeBeanAnnotation someBean;
}

注入bean

  • xml 显式配置

    构造器注入 属性注入

  • 注解显式配置

    @Autowired

  • 隐式的bean发现机制和自动装配

注解注入在XML注入之前执行,因此同时使用这两种方式注入时,XML配置会覆盖注解配置。

xml自动装配

autowire="byName"

  • no 默认非自动装配
  • byName
  • byType
  • constructor

设置元素的autowire-candidate属性为false;可以排除他们自动注入到其他bean,但是它本身是可以被自动注入的。

通过设置袁术的primary属性为true来指定单个bean定义作为主候选bean。

常用注解

@Autowired注解可以应用在setter, 构造函数上或者属性上,默认required 属性是 true 的。

@Required注解应用于bean的setter方法, 声明在配置时必须赋值
@Qualifier注解。可以将qualifier关联指定参数.
public void prepare(@Qualifier("main")MovieCatalog movieCatalog){};

qualifier值,只是为了缩小类型匹配的范围;他们并不能作为引用的bean的唯一标示符。

@PostConstruct @PreDestroy bean的回调
@Repository, @Service, @Controller 各层代码注解,分别注解dao , service, layers表现层. 他们默认id是类名首字母小写
@Repository能自动转换异常。 @Component是他们的元注解。
@Scope("prototype") 指定bean作用域scope

@Import注解也允许从其他配置类(@Configuration)中加载@Bean定义。 @Import(ConfigA.class) 这样相当与使用AnnotationConfigApplicationContext 或者@ConponentScan @ImportResource("classpath:/com/acme/properties-config.xml") 配置类(@Configuration)中引用XML配置。
@Configuration注解的类表明该类的主要目的是作为bean定义的源。 @Configuration类本身也会注册为一个bean定义,通过CGLIB创建一个子类。类内所有的@Bean注解的方法也会注册为bean定义。类不能是final.
@Bean注解用于表明一个方法将会实例化、配置、初始化一个新对象,该对象由Spring IoC容器管理。

@Primary该bean首选

@ComponentScan 自动探测类和自动注册bean定义

注解方式:在@Configuration注解的类上增加@ComponentScan,其basePackages属性就是上面两个类的所在的父级包路径。

xml: 使用将会隐式的启用

@Configuration 只是声明该类为bean源,还需要@ConponentScan 去加载到context 里面。或者在声明AnnotationConfigApplicationContext 的时候都加到里面。

bean 作用域

singleton 默认的。一个bean定义,在一个IoC容器内只会产生一个对象。
prototype原型 每次都创建一个新的实例
request 每个HTTP请求都产生新的实例。Spring web上下文环境中有效。
session 生命周期在HTTP 会话期间。Spring web上下文环境中才有效。

一般来说 ,原型bean用于有状态bean,单例bean用于无状态bean。一个数据访问对象(DAO)通常不会配置成原型作用域,因为通常DAO不会持有任何会话状态。

如果想将HTTP request作用域(request,session,globalSession)bean注入给其他singleton bean,就得给作用域bean(request或者session)注入一个AOP代理用来替换作用域bean。容器会创建一个代理对象,该对象拥有和原对象完全相同的public方法。

因为容器初始化的时候singleton 需要加载,但此时session bean还未存在


    


       //代理只会拦截public方法调用。非public方法不会“呼叫转移”给实际的作用域bean。
 

若是在Java代码中配置bean,使用@Scope注解并配置其proxyMode属性.默认配置是没有代理ScopedProxyMode.NO, 但是你可以设置ScopedProxyMode.TARGET_CLASS或者ScopedProxyMode.INTERFACES。 @Scope(value ="session", proxyMode =ScopedProxyMode.TARGET_CLASS)

@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.TARGET_CLASS)

高级装配

Environment环境

Environment环境在容器中是一个抽象的集合,是指应用环境的2个方面: profiles 和 properties.

profile

bean定义profile是核心容器内的一种机制,该机制能在不同环境中注册不同的bean。
比如:

  • 开发期,QA或者产品上使用来自JNDI的不同数据源
  • 开发期使用监控组件,当部署以后则关闭监控组件
Profile定义
  1. @Profile可以用于元数据注解,注解方法(@Bean 注解的方法),注解类(@Configuration。 i.g. @Profile("production")
  2. XML中的beans 也有profile 属性。 profile="dev"

profile定义的bean都将被忽略,除非该profile被激活

如果给定的profile,使用了NOT操作(!)前缀,若当前profile未被激活则注解元素将会注册。对于@Profile({"p1", "!p2"}),在profile 'p1'被激活或者'p2'未激活时,发生注册。

激活profile

1.编程式开启方式

AnnotationConfigApplicationContext ctx =new AnnotationConfigApplicationContext(); 
ctx.getEnvironment().setActiveProfiles("dev");

2.使用spring.profiles.active属性激活配置 。
spring.profiles.active="profile1,profile2"
3.servlet初始化参数,web上下文参数,环境变量等等

PropertySource

Spring的环境抽象可以检索一系列的property sources属性配置文件。
Spring的StandardEnvironment配置了2个PropertySource对象-其一是JVM系统properties(System.getProperties()),另一个是一组系统环境变量(System.getenv())。

@Component
@PropertySource(value ="classpath:com/spring/Property/quartz.properties")
public class MyProperty{

//为了支持spring el 表达式
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfig() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}
@ContextConfiguration(classes = {MyProperty.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class PropertyTest {

    @Autowired
    private Environment env;

    @Test
    public void test(){
        System.out.print(env);
    }
}

条件化的bean

使用@Conditional可以用到带有@Bean 注解的方法上,指向一个实现了Condition接口的类,根据matches方法返回的boolean值确定是否注册这个bean

 @Conditional(MyConditional.class)
    @Bean(name = "b2")
    public ClientServiceImpl getService() {
       return ...;
    }

方法替换

设置replaced-method元素,就可用其他实现来替换已经部署的bean中存在的方法实现。


     
      
        String
    


  

方法注入

在容器范围内查找一个bean作为方法返回结果

 
      
 
   

       
public abstract class UserManager {
    protected abstract User getUser();

    public void run(){
        User user = getUser();
        System.out.println(user);
    }
}

class User{
    String name;
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}

事务

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE)

事务传播行为

PROPAGATION_SUPPORTS 有事务就用,没有就不用
PROPAGATION_NOT_SUPPORTED 不用事务,如果当前有就挂起事务
PROPAGATION_MANDATORY 有就用,没有就抛异常
PROPAGATION_NEVER 有事务就跑出异常
PROPAGATION_REQUIRED 有就用,没有就新建
PROPAGATION_REQUIRES_NEW 新建一个,如果当前有就挂起当前事务
Propagation.NESTED 没有就新建,有就当前事务中再嵌套事务运行 — 嵌套事务中可以定义储存点,因此可以独立于外部的Transaction而进行rollback

数据库的隔离级别

数据库事务隔离级别:一个事务对数据库的修改与并行的另一个事务的隔离程度。

并发访问数据库存在的问题

  1. 脏读:事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的行就无效,也就是脏数据。
  2. 不可重复读取:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,然后T1再次查询,发现与第一次读取的记录不同,这称为不可重复读。
  3. 幻读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。

不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读和幻读是读取了前一事务提交的数据。

不可重复读的重点是修改,幻读的重点在于新增或者删除

四种隔离级别

  1. Read uncommitted (读未提交):最低级别,任何情况都无法保证。
  2. Read committed (读已提交):可避免脏读的发生。
  3. Repeatable read (可重复读):可避免脏读、不可重复读的发生。
  4. Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Read committed (读已提交)和Serializable (串行化)级别这两种级别,其中默认的为Read committed级别。

设置数据库的隔离级别一定要是在开启事务之前,隔离级别的设置只对当前链接有效。

例子:

Spring_第4张图片

左面是事务T1,右面是事务T2,因为T2级别为SERIALIZABLE,所以即使事务T1在提交了数据之后,事务T2还是看不到T1提交的数据,幻想读和不可重复读都不允许了。

那如何能查看到T1新增的记录呢? 上面T1和T2是并发执行,在T1执行insert的时候如果事务T2已经开始了,因为T2级别是SERIALIZABLE,所以T2所查询的数据集是T2事务开始前数据库的数据。即事务T1在事务T2开始之后的insert和update操作的影响都不会影响事务T2。现在重新开启一个事务T3 就可以看到T1新增的记录了。

AOP

五种类型的通知

  • @Before
  • @After
  • @AfterReturning 成功执行后
  • @AfterThrowing 抛异常后
  • @Around

Spring 的AspectJ自动代理只是使用AspectJ作为创建切面的指导,切面仍然是基于代理的。本质上,它仍然是Spring 基于代理的切面。如果要使用全部AspectJ功能需要不依赖Spring 来创建切面并且在运行时使用AspectJ

@Aspect
@Component
public class MyInterceptor implements Ordered {

    //第一个* 代表任意返回值, .. 代表任意参数
    //符合条件的才被切入
    @Pointcut("execution(* com.spring.aop.MyService.*(..))")
    private void anyMethod(){}//定义一个切入点

    @Before("anyMethod() && args(name,param2)")
    public void doAccessCheck(String name,int param2){
        System.out.println(name);
        System.out.println(param2);
        System.out.println("前置通知");
    }

    @AfterReturning("anyMethod()")
    public void doAfter(){
        System.out.println("后置通知");
    }
}
 
    
    

    
    
 public static void main(String[] args) {
   ApplicationContext ctx = new ClassPathXmlApplicationContext("/aop.xml");
   MyService bean = (MyService) ctx.getBean("serviceBean");
   bean.update("param1",2);
}

切面的织入顺序

order 的值越小,说明越先被执行

@Component
@Aspect
public class MyAspectJ implements Ordered{
...
    @Override
    public int getOrder() {
        return 1;
    }
}


这样就实现了我们自己写的aop在事务介入之前就执行了!

JSR-330

Spring3.0开始,Spring提供了对JSR-330标准注解(依赖注入)的支持。这些注解以Spring注解相同的方式被扫描。


    javax.inject
    javax.inject
    1

Spring javax.inject.*
@Autowired @Inject @Inject 没有required属性
@Component @Named
@Scope("singleton") @Singleton JSR-330默认的作用域类似于Spring的prototype原型作用域。为了保持Spring的一致性,在Spring容器中的JSR-330的bean声明,默认是singleton单例。除了singleton,若要设置作用域,得使用Spring的@Scope注解。javax.inject也提供了一个@Scope注解。然而,这个注解仅仅是为了让你创建自定义注解用的译注,也就是元注解的源码注解。
@Qualifier @Named
@Value 无等价注解
@Required 无等价注解
@Lazy 无等价注解

others

spring-web 初始化配置 (spring-mvc 不需要这个)
为了支持request,sesssion,global session这种级别bean的作用域(web作用域bean),在定义bean之前需要一些初始化的配置。

若使用 Spring Web MVC访问这些作用域bean,实际上是使用Srping DispatcherServlet类或者DispatcherPortlet类处理request,则无需特别配置:DispatcherServlet 和 DispatcherPortlet已经暴露了所有的相关状态。
非Spring的DispacherServlet处理请求(比如,JSF或者Struts2)。

  1. 小于2.5 的Servlet web容器: RequestContextFilter
  2. Servlet 2.5的web容器则需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener。
  3. Servlet 3.0+,这些设置可以通过编程式方式使用WebApplicationInitializer接口完成。

DispatcherServlet,RequestContextListener,RequestContextFilter都是做相同的事,也就是绑定HTTP request对象到服务的Thread线程中,并开启接下来 用到的session-scoped功能。

SSH 需要上面的spring + spring-web +struts2配置。如果是用spring-mvc 只需spring + spring-mvc 自己的servlet (DispatcherServlet)配置就可以了。

你可能感兴趣的:(Spring)