Spring学习

作者: newcih | 来源:发表于2017-05-22 06:50 被阅读0次

    [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 class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    

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

    <context:annotation-config/>
    
    • 自动装配一个或多个兼容类型的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元素<context:component-scan>,要求Spring扫描这些注解。在这个元素中,你必须指定扫描组件所用的包。然后指定的包和子包都将被扫描。Spring将把类名第一个字符小写,对其余部分采用Camel-cased命名法组成Bean名称。

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

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

    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注解的类:

    <beans ...>
        <context:component-scan base-package="com.apress.sequence">
            <context:include-filter type="regex"
                expression="com\.apress\.sequence\..*Dao.*"/>
        </context:component-scan>
    </beans>
    

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

    命名检测到的组件

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

    设置Bean作用域

    在Spring 2.x或者更新版本中,Bean的作用域在<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 属性,指定回调方法名称。
    <bean id="cashier1" class="..." init-method="openFile" destory-method="closeFile"/>
    
    • 在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元素<aop:aspectj-autoproxy>,就可以启用Spring IoC容器中的AspectJ注解支持。然后,Spring将自动为匹配你的AspectJ aspect的所有Bean创建代理。对于接口不可用或者没有用于应用设计中的情况,可以依靠CGLIB创建代理。为了启用CGLIB,必须在<aop:aspectj-autoproxy>中设置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学习

        本文链接:https://www.haomeiwen.com/subject/aruvxxtx.html