Spring学习

[TOC]

CGLIB与JDK动态代理

使用JDK创建代理有一个限制,即它只能为接口创建代理实例,这一点可以从Proxy的接口方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)中看得很清楚;第二个传入参数interfaces就是需要代理实例实现的接口列表。对于没有通过接口定义业务方法的类,如何动态创建代理实例呢?JDK动态代理技术显然已经黔驴技穷,CGLIB作为一个替代者,填补了这些空缺。

proxy-target-class

proxy-target-class这个属性是决定基于接口还是基于类的代理被创建,默认为false。如果proxy-target-class属性值被设置为true,那么基于类的代理将起作用(这个时候需要cglib库)。如果proxy-target-class被设置为false或者这个属性被省略,那么表示使用JDK动态代理织入增强,但是,即使设置为false,如果目标类没有声明接口,则Spring将自动使用CGLIB动态代理。

通俗理解:当要使用实现了某个接口的类让Spring来生成Bean时,无需再AOP配置中添加proxy-target-class,因为它默认是false。但如果要使用一个指定的类,让Spring来生成Bean,并使用它的某个方法时,需要在AOP配置上加上一句proxy-target-class=true,否则使用CGLIB,会出现

Java.lang.ClassCastException:com.sun.proxy.$Proxy6 cannot be cast to glut.daoImpl.DAOImpl

类似的错误。

使用@Autowired和@Resource自动装配Bean

解决方案

从Spring2.5起,可以通过@Autowired或者@Resource注解一个设值方法,构造程序,字段甚至任意方法自动装配特定的属性。

工作原理

为了要求Spring自动装配具有@Autowired或者@Resource注解的属性,你必须在IoC容器中注册一个AutowiredAnnotationBeanPostProcess实例。如果你使用一个Bean工厂,就必须通过API注册这个Bean后处理器,否则,你只能在你的应用上下文里声明一个实例。


你也可以简单地在Bean配置文件中包含元素,这将自动注册一个AutowiredAnnotationBeanProcessor实例。


  • 自动装配一个或多个兼容类型的Bean
    @Autowired注解还可以应用到一个字段,即使这个字段没有声明为public。这样,你不能省略这个字段的设值方法或者构造程序的声明。Spring通过反射把匹配的Bean注入这个字段。但是,用@Autowired注解非公开的字段将降低代码的可测试性,因为代码将很难进行单元测试(黑盒测试无法使用模拟对象之类的方式操纵这一状态)
  • 使用限定符的按类型自动装配
    默认情况下,按照类型的自动装配在IoC容器中有超过一个类型兼容的Bean时无效。但是,Spring允许你指定一个候选Bean,这个Bean的名称在@Qualifier注解中提供。
@Autowired
@Qualifier("datePrefixGenerator")
  • 按照名称自动装配
    需要按照名称自动装配Bean属性,可以用JSR-250 @Resource注解为一个设值方法,构造程序或者字段加上注解。默认情况下,Spring将试图找到一个与属性同名的Bean。但是你可以显式地在name属性中指定Bean名称
@Resouce(name = "datePrefixGenerator")

从Classpath中扫描组件

有了应用到组件类的典型化注解,就能通过声明一个XML元素,要求Spring扫描这些注解。在这个元素中,你必须指定扫描组件所用的包。然后指定的包和子包都将被扫描。Spring将把类名第一个字符小写,对其余部分采用Camel-cased命名法组成Bean名称。

Camel-cased命名法:当一个命名包含多个单词时,每个单词的第一个字母大写

因此,下面的语句是有效的(假定你已经实例化了一个包含元素的应用上下文)

SequenceService service = (SequenceService)context.getBean("sequenceService");

注意,这个元素还将注册一个AutowiredAnnotationBeanPostProcessor实例,这个实例能够自动装配带有@Autowired注解的属性。

过滤扫描的组件

默认情况下,Spring将检测所有用@Component,@Repository,@Service,@Controller或者本身加上@Component注解的自定义注解类型。你可以应用一个或多个包含/排除过滤器自定义这一扫描。Spring支持4中过滤器表达式。annotation和assignable类型用于指定过滤的注解类型和类/接口。regex和aspectj类型允许指定正则表达式和AspectJ切入点表达式匹配类。你还可以用use-default-filters属性禁用默认过滤器。
例如,下面的组件扫描包含了所有名称中包含Dao或Service的类,排除带有@Controller注解的类:


    
        
    

因为你已经应用了include过滤器检测所有名称包含Dao或者Service的类,SequenceDaoImpl和SequenceService组件就能在没有典型化注解的情况下被自动检测出来。

命名检测到的组件

默认情况下,Spring将非限定类名的第一个字符改为小写来命名检测到的组件。你可以在典型化注解值中显式地指定组件的名称。

设置Bean作用域

在Spring 2.x或者更新版本中,Bean的作用域在元素的scope属性中设置。默认情况下,Spring为IoC容器中声明的每个Bean创建一个实例,这个实例将在整个IoC容器的范围内共享。所有后续的getBean()调用和Bean引用都将返回这个独特的Bean实例。这个作用于称为singleton,是所有Bean的默认作用域。

作用域 描述
Singleton 每个Spring IoC容器创建一个Bean实例
Prototype 每次请求时创建一个新的Bean实例
Request 为每个HTTP请求创建一个Bean实例,仅在Web应用上下文中有效
Session 为每个HTTP会话创建一个Bean实例,仅在Web应用上下文中有效
GlobalSession 为每个全局HTTP会话创建一个Bean实例,仅在门户应用上下文有效

自定义Bean初始化和析构

下面的列表展示了Spring IoC容器管理Bean周期的步骤。这个列表将随着IoC容器更多特性的引入而扩展。

  1. 构造程序或者工厂方法创建Bean实例
  • 想Bean属性设置值和Bean引用
  • 调用初始化回调方法
  • Bean就绪
  • 容器关闭时,调用析构回调方法

Spring有三种识别初始化和析构回调方法的方式。

  1. 你的Bean可以实现 InitializingBeanDisposableBean 生命周期接口,并且实现用于初始化和析构的 afterPropertiesSet()destory() 方法
public class Cashier implements InitializingBean, DisposableBean {
    ...
    public void afterPropertiesSet() throws Exception {
        openFile();
    }
    public void destory() throws Exception {
        closeFile();
    }
}

但是,实现这些专利接口将会使你的Bean变成Spring专用的,无法再Spring IoC容器之外重用

  • 你可以在Bean声明中设置 init-methoddestory-method 属性,指定回调方法名称。

  • 在Spring 2.5或更高版本中,你还可以用生命周期注解 @PostContruct@PreDestory 注解初始化和析构回调方法,这两个注解在JSR-250定义,然后你可以在IoC容器中注册 CommonAnnotationBeanPost Processor 实例来调用这些回调方法。
public class Cashier {
    ...
    @PostConstruct
    public void openFile() throws IOException {...}
    
    @PreDestory
    public void closeFile() throws IOException {...}
}

启用Spring的AspectJ注解支持

在Bean配置文件中定义一个空的XML元素,就可以启用Spring IoC容器中的AspectJ注解支持。然后,Spring将自动为匹配你的AspectJ aspect的所有Bean创建代理。对于接口不可用或者没有用于应用设计中的情况,可以依靠CGLIB创建代理。为了启用CGLIB,必须在中设置proxy-target-class=true属性。

用AspectJ注解声明aspect

  1. 前置通知(Before advice
    为了创建在程序特定执行点之前处理横切关注点的前置通知,你可以使用@Before注解,并将切入点表达式作为注解值
  • 最终通知(after advice
    在连接点结束之后执行,不管返回结果还是抛出异常。

  • 后置通知(after returning advice
    最终通知不管连接点正常返回还是抛出异常都执行。如果你希望仅当连接点返回时记录,应该用后置通知

  • 异常通知(after throwing advice
    仅当连接点抛出异常时执行

  • 环绕通知(around advice
    获得连接点的完全控制,这样可以在一个通知中组合前述的通知的所有行动。甚至可以控制合适以及是否继续原来的连接点执行。注意环绕通知,连接点的参数类型必须是ProceedingJoinPoint。这是JoinPoint的一个子接口,允许你控制合适继续原始连接点。

@Aspect
public class CalculatorLoggingAspect {
    @Around("execution(* *.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    ....
    }
}

你可能感兴趣的:(Spring学习)