【阅读笔记】《轻量级JavaEE企业应用实战》(第八章)

本章主要包含Spring的AOP和Spring和Hibernate和Struts整合

后处理器

Bean后处理器会在Bean实例创建成功之后,对Bean实体进行进一步的增强处理。
Bean后处理器必须实现BeanPostProcessor接口,该接口中包含两个方法postProcessAfterInitialization和postProcessBeforeInitialization,分别代表初始化之后执行和初始化之前执行,以下是一个小例子

package com.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("Bean后处理器的postProcessAfterInitialization方法");
        System.out.println("beanName=" + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("Bean后处理器的postProcessBeforeInitialization方法");
        System.out.println("beanName=" + beanName);
        return bean;
    }

}

还可以通过bean标签中init-method设置这个bean的初始化方法,用destory-method来设置这个bean的销毁方法,这些方法是写在这个bean所对应的类中的。

容器后处理器

除了创建Bean的后处理器之外还可以创建Spring容器的后处理器,我们只需要实现BeanFactoryPostProcessor接口即可,它里面有一个postProcessBeanFactory方法,方法参数ConfigurableListableBeanFactory就是Spring容器

属性占位符配置器

想在xml文件中使用.propertiest文件中的内容就可以看看下面的代码了。
在application.xml文件中加入

    <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
        <property name="locations">
            <list>
                <value>classpath:dbconn.properties</value>
            </list>
        </property>
    </bean>
    
    <!-- 数据库相关配置 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close"
        p:driverClass="${dataSource.driverClass}"
        p:user="${dataSource.user}"
        p:password="${dataSource.password}"
        p:jdbcUrl="${dataSource.jdbcUrl}"
        p:maxPoolSize="${dataSource.maxPoolSize}"
        p:minPoolSize="${dataSource.minPoolSize}"
        p:initialPoolSize="${dataSource.initialPoolSize}"
        p:maxIdleTime="${dataSource.maxIdleTime}"
    />

之后在src文件夹下创建dbconn.properties,它的文件内容是

dataSource.driverClass = com.mysql.jdbc.Driver
dataSource.user = root
dataSource.password = root
dataSource.jdbcUrl = jdbc:mysql://localhost/spring
dataSource.maxPoolSize = 40
dataSource.minPoolSize = 2
dataSource.initialPoolSize=2
dataSource.maxIdleTime=30

Spring的“零配置”支持

如果不想用xml配置的话,也可以用annotation,在Spring中提供了一下几个annotation来配置Spring Bean类

  • @Component:标注一个普通的Spring Bean类
  • @Controller:标注一个控制器组件类
  • @Service:标注一个业务逻辑组件类
  • @Repository:DAO组件类
    配置好了这些annotation之后只需要在application.xml文件中加入
    <context:component-scan base-package=""></context:component-scan>
    来指定Spring自动搜索这些包下的Java类,并以注解来生成Bean实例。使用的时候要注意的是:
  • @Component可以带一个字符串类型的参数用作bean的id
  • @Scope可以带“prototype”或者别的范围来指定bean的使用范围
  • @Resourceo(name="beanName")可以用来配置依赖
  • @PostConstruct和@PreDestroy来配置定制生命周期的行为,比如说init和close
  • @Lazy可以带一个boolean类型的参数,如果为true就不会预初始化该Bean
  • @DependsOn({beanName1,beanName2})可以强制初始化bean1和bean2,就算bean1、bean2设置成@Lazy(true)
  • @Autowired,是用来自动装配的,可以在bean的set方法上加入@Autowired或者在构造函数上加入,哪怕参数有多个也不要紧,Spring就会自动按照类型搜索出合适的bean。@Autowired可以精确的利用类型(泛型)执行自动装配,但是如果有两个不同的bean他们的类型是同样的可这么办呢?这时我们可以通过@Qualifier来通过bean的id自动装配

资源访问

访问类加载路径里的资源

book.xml文件放在src文件下

        ClassPathResource resource = new ClassPathResource("book.xml");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
        SAXReader reader = new SAXReader();
        Document document = reader.read(resource.getFile());
        Element root = document.getRootElement();
        System.out.println("root="+root.getText());
        List list = root.elements();
        for(int i = 0; i < list.size(); i++) {
            Element book = (Element) list.get(i);
            System.out.println(i+":"+book.getText());
        }

访问文件系统里的资源

book.xml在与WebContent文件夹同一级

FileSystemResource resource = new FileSystemResource("book.xml");

访问网络资源

网络资源需要使用InputStream流来读取数据,不可以直接getFile的

            UrlResource resource = new      UrlResource("http://hxu0170466.my3w.com/book.xml");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
        SAXReader reader = new SAXReader();
        Document document = reader.read(resource.getInputStream());
        Element root = document.getRootElement();
        System.out.println("root="+root.getText());
        List list = root.elements();
        for(int i = 0; i < list.size(); i++) {
            Element book = (Element) list.get(i);
            System.out.println(i+":"+book.getText());
        }

与之类似的还有ServletContextResource访问相对于ServletContext路径下的资源、InputStreamResource访问输入流资源、ByteArrayResource访问字节数组资源。

将Resource作为属性

如果某一个Bean中需要使用某个文件的内容,那么我们就可以在这个Bean里面自动注入一个Resource实例,在配置bean的<bean>标签中加入 p:res="classpath:book.xml"(这个bean有一个Resource类型的res属性)这样在这个bean中就可以获取到类加载路径下的book.xml文件的内容了。

在ApplicationContext使用资源

Resource r = applicationContext.getResource("book.xml")

就可以获得到book.xml的文件内容了,这是以applicationContext的访问策略来判定的,applicationContext是通过classpath来创建的那么book.xml就应该放在class加载路径下。

Spring的AOP

AOP(Aspect Orient Programming),是指面向切面编程。与面向对象编程做对比的话可以这样说,面向对象是从静态的角度考虑程序结构,面向切面编程则是从动态角度考虑程序的运行过程。

为什么需要AOP

AOP可以在不修改源代码的前提下增加系统的功能,如果只是用OOP的话如果要在某一个模块上添加一个用户合法验证的话就需要在需要验证的类里面添加验证代码,如果这种类有很多的话那么修改起来是很麻烦的,这时我们就可以通过使用AOP的方式添加一些新的功能(虽然我觉得拦截器也可以做到同样的效果),常常用AOP来处理一些具有横切性质的系统级服务,如事物管理、安全检查、缓存和对象池管理等。总之,AOP要达到的效果是:保证在程序员不修改源代码的前提下,位系统中业务组件的多个业务方法添加某种通用的功能。但AOP的本质是,依然要去修改业务组件的多个业务方法的源代码——只是这个修改由AOP框架完成,而不是程序员。

AOP的基本概念

AOP框架的两个特征
  • 各步骤之间的良好隔离性
  • 源代码无关性
面向切面编程的一些术语
  • 切面(Aspect):切面用于组织多个Advice,Advice放在切面中定义
  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或者异常的抛出。<font color="red">Spring AOP中,连接点总是方法的调用</font>
  • 增强处理(Advice):AOP框架在特定的切入点执行的增强处理。处理有“around”、“before”和“after”等类型
  • 切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。
  • 引入:将方法或字段添加到被处理的类中。
  • 目标对象:被AOP框架进行增强处理的对象。
  • AOP代理:AOP框架创建的对象,简单的说,代理就是目标对象的加强。
  • 织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象(AOP代理)的过程就是织入。

Spring的AOP支持

Spring AOP采用的是基于代理的AOP方案,这与AspectJ采用的编译时增强的解决方案不同。
AOP代理的方法 = 增强处理 + 目标对象的方法

基于注解的“零配置”方式(小示例)

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"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.0.xsd">
    <bean id="steelAxe" class="service.SteelAxe"/>
    <bean id="british" class="service.British" p:axe-ref="steelAxe"/>
    <context:component-scan base-package="service,com.aspect">
        <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
    </context:component-scan>
    <aop:aspectj-autoproxy/>
    <aop:config proxy-target-class="true"/>
</beans>

Birtish.java

package service;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class British implements Person{
    private Axe axe;
    
    public void setAxe(Axe axe) {
        System.out.println("Spring调用setAxe执行依赖注入");
        this.axe = axe;
    }
    @Override
    public String useAxe() {
        // TODO Auto-generated method stub
        System.out.println(axe.chop());
        return "Slience";
    }
}

Aspect类

package com.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AuthAspect {
    @Before("execution(* service.*.*(..))")
    public void before() {
        System.out.println("before");
    }
    @After("execution(* service.*.*(..))")
    public void after() {
        System.out.println("after");
    }
    @AfterReturning(returning="rvt", pointcut="execution(* service.*.*(..))") 
    public void afterReturning(Object rvt) {
        System.out.println("afterReturning,返回值是:" + rvt);
    }
    @Around("execution(* service.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around,执行之前");
        Object rvt = joinPoint.proceed();
        System.out.println("around,执行之后,返回值是:" + rvt);
        //around还可以对返回值进行修改
        rvt = "Hello World";
        return rvt;
    }
}

其中功能比较强大的就是@Around了,它既可以在目标方法之前织入也可以在目标方法之后织入而且可以决定目标方法在什么情况下执行,甚至可以完全阻断目标方法的执行,但是如果要使用@Around最好在线程安全的情况下使用,平时还是使用@After和@Before,但是如果要改变返回值的话只有@Around可以实现了。
如果有多个Aspect可以使用@Order(value=1)来实现按照顺序执行,value越大就越先执行。
如果需要获取目标方法的调用参数可以这样写@AfterReturning("execution(* service.*.*(..)) && args(arg0, arg1)")之后在下面的方法这样就可以获得那个方法的第一个和第二个参数了public void before(Object rvt, String arg0, String arg1)
如果觉得每次写execution(* service.*.*(..)) && args(arg0, arg1)太麻烦了可以在Aspect类里面写上这个

    @Pointcut("execution(* service.*.*(..))")
    public void myPointcut(){};

之后调用这个execution可以这样@Before("myPointcut()"),如果别的Aspect类也想用这个Aspect类的myPointcut()所定义的execution的话,可以这样写@Before("AuthAspect.myPointcut()")用类名.方法名的方式实现

基于XML配置文件的管理方式

如果将上面使用注解的例子改成使用XML配置的话就是这样的

    <bean id="authAspect" class="com.aspect.AuthAspect"/>
    <aop:config proxy-target-class="true">
        <aop:aspect id="authAspect" ref="authAspect">
            <aop:before method="before" pointcut="execution(* service.*.*(..))"/>
            <aop:after method="after" pointcut="execution(* service.*.*(..))"/>
        </aop:aspect>
    </aop:config>

其中的method指的是方法的名字

Spring3.1新增的缓存机制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容