- 浏览: 1012396 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (826)
- 硬件 (8)
- 软件 (24)
- 软件工程 (34)
- JAVA (229)
- C/C++/C# (77)
- JavaScript (8)
- PHP (1)
- Ruby (3)
- MySQL (14)
- 数据库 (19)
- 心情记事 (12)
- 团队管理 (19)
- Hadoop (1)
- spring (22)
- mybatis(ibatis) (7)
- tomcat (16)
- velocity (0)
- 系统架构 (6)
- JMX (8)
- proxool (1)
- 开发工具 (16)
- python (10)
- JVM (27)
- servlet (5)
- JMS (26)
- ant (2)
- 设计模式 (5)
- 智力题 (2)
- 面试题收集 (1)
- 孙子兵法 (16)
- 测试 (1)
- 数据结构 (7)
- 算法 (22)
- Android (11)
- 汽车驾驶 (1)
- lucene (1)
- memcache (12)
- 技术架构 (7)
- OTP-Erlang (7)
- memcached (17)
- redis (20)
- 浏览器插件 (3)
- sqlite (3)
- Heritrix (9)
- Java线程 (1)
- scala (0)
- Mina (6)
- 汇编 (2)
- Netty (15)
- libevent (0)
- CentOS (12)
- mongod (5)
- mac os (0)
最新评论
-
kingasdfg:
你这里面存在一个错误添加多个任务 应该是这样的 /** * ...
Quartz的任务的临时启动和暂停和恢复【转】 -
kyzeng:
纠正一个错误,long型对应的符号是J,不是L。
Jni中C++和Java的参数传递 -
zhaohaolin:
抱歉,兄弟,只是留下作记录,方便学习,如果觉得资料不好,可以到 ...
netty的个人使用心得【转】 -
cccoooccooco:
谢谢!自己一直以为虚机得使用网线才可以与主机连接呢。。
主机网卡无网线连接与虚拟机通信 -
yuqilin001:
要转别人的东西,请转清楚点嘛,少了这么多类,误人子弟
netty的个人使用心得【转】
AOP技术
1.引出问题
建立spring
_04_aop项目,在该项目下有一个UserDao接口,代码
如下:
package com.asm.dao;
public interface UserDao {
void save();
void update();
}
该接口的实现类UseDaoImp,代码如下:
package com.asm.dao.impl;
import com.asm.dao.UserDao;
public class UserDaoImp implements UserDao {
private String username;
public UserDaoImp() {
}
public UserDaoImp(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
@Override
public void save() {
System.out.println("save method is called:" + username);
}
@Override
public void update() {
System.out.println("update method is called" + username);
}
}
需求如下:如果实现类的username!=null,才可以调用save与update方法,为null则不能调用。当然要解决此问题,可以在save
与update方法内部进行判断,但是如果在方法内部进行判断,代码则失去了灵活性,如果以后的需求改变,比如变成username.equals时,则
又要在update/save方法中重新进行一次判断。如果save/update这样的方法很多,这样就会很麻烦。其实要解决此问题,可以通过动态
代理技术实现。这里的需求其实就是根据要求来拦截一些业务方法,这种编程问题称之为横切性关注点。
代理的目标对象必须实现了一个接口---横切关注点
2.动态代理实现[JDK]
建立代理工厂ProxyFactory,代码如下:
package com.asm.dao.impl.factory;
public class ProxyFactory implements InvocationHandler {
private Object target;
public Object createUserDaoImp(Object target) {
this.target = target;
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
UserDaoImp udi = (UserDaoImp) target;
Object result = null;
if (udi.getUsername() != null) {
result = method.invoke(target, args);
}
return result;
}
}
简析动态代理:此类根据传递的target对象来生成此目标对象的代理对象,需要强调的是动态代理技术所要代理的对象必须实现一个接口。
newProxyInstance参数说明:第一个参数是目标对象的类装载器,第二个参数是目标对象的接口,第三个参数是回调对象,生成的代理对象要执行
方法时就是依靠这个回调对象的invoke方法来进行目标对象的方法回调。关于动态代理的其它细节不在此讨论。
建立junit测试代码,内容如下:
public class AopProxyTest {
@Test //用户
名为空,不执行方法
public void testProxy(){
ProxyFactory pf=new ProxyFactory();
UserDao ud=(UserDao) pf.createUserDaoImp(new UserDaoImp());
ud.save();
}
@Test //用户名为不为空,才执行save方法
public void testProxy2(){
ProxyFactory pf=new ProxyFactory();
UserDao ud=(UserDao) pf.createUserDaoImp(new UserDaoImp("张某"));
ud.save();
}
}
3.cglib实现代理
JDK的Proxy实现代理要求被代理的目标对象必须实现一个接口,而如果目标对象没有实现接口则不能使用Proxy来代理。其实也可以借助cglib来实现代理。操作步骤如下
步骤一、建立UserDaoImp2类,它与UserDaoImp的唯一区别就是没有实现任何接口。
步骤二、导入cglib.jar包,创建cglib代理工厂,代码如下:
package com.asm.dao.impl.factory;
public class CglibFactory implements MethodInterceptor {
private Object target;
public Object createUserDaoImp2(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// cglib创建的代理对象,其实就是继承了要代理的目标类,然后对目标类中所有非final方法进行覆盖,但在覆盖方法时会添加一些拦截代码。
enhancer.setCallback(this); //注册回调器
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
UserDaoImp2 udi = (UserDaoImp2) target;
Object result = null;
if (udi.getUsername() != null) {
// 前置通知
try {
result = methodProxy.invoke(target, args);
// 后置通知
} catch (Exception e) {
e.printStackTrace();
// 例外通知
} finally {
// 最终通知
}
}
return result;
}
}
说明:注意注释的通知,通知就是拦截到方法后执行的一些代码,比如前置通知,就是说在回调目标方法时执行的一些操作。
步骤三、建立测试代码(省略,和五.2的测试代码相似)
4.aop理论知识
横切性关注点:对哪些方法拦截,拦截后怎么处理,这些关注就称之为横切性关注点
切面(aspect):类是对物体特征的抽象,而切面是指对横切性关注点的抽象。
连接点(joinpoint):被拦截到的点,因为spring只支持方法类型的连接点,所以在spring中连接点指的就是被拦截到的方法。实际上连接点还可以是字段或构造器。
切入点(pointcut):对连接点进行拦截的定义。
通知(advice):所谓通知就是指拦截到连接点之后的要执行的代码。通知分为前置、后置、异常、最终。环绕通知五类。
目标对象:代理的目标对象。
织入(weave):将切面应用到目标对象并导致代理对象创建的过程
引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。
5.基于spring的AOP实现
步骤一、导入spring开发
的基本包(包括切面及注解包)
步骤二、编写切面类TheInterceptor,代码如下:
package com.asm.dao.impl.factory;
@Aspect
public class TheInterceptor {
@Pointcut("execution (* com.asm.dao.impl.UserDaoImp.*(..))")
// 声明一个切入点(第一个*后要留一个空格
)
private void anyMethod() {
}
@Before("anyMethod()")// 前置通知
public void before() {
System.out.println("前置通知");
}
@AfterReturning("anyMethod()")//后置通知
public void afterReturning(){
System.out.println("后置通知");
}
}
简析:Aspect注解声明此类为一个切面类,Pointcut注解用来声明一个切入点,括号中的参数是切入点的表达式,这里的表达式的意思是对
UserDaoImp类的所有方法进行拦截。关于切入点表达式后面会有详细的说明。anyMethod是为切入点起一个名字,后面的“通知”都要依赖这个
名字。
步骤三、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:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
-
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- <aop:aspectj-autoproxy /><!-- 开启切面编程功能 -->
- <bean id="userDaoImp" class="com.asm.dao.impl.UserDaoImp" />
- <bean id="theInterceptor"
- class="com.asm.dao.impl.factory.TheInterceptor" />
- </beans>
说明:要想切面类起作用,首先要把切面类纳入spring容器管理。
步骤四、编写junit测试单元
@Test
public void base() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// UserDaoImp udii=(UserDaoImp) ctx.getBean("userDaoImp");
UserDao ud = (UserDao) ctx.getBean("userDaoImp");
System.out.println(ud.getClass().getName());
ud.save();
}
说明:由于开启了切面编程功能,所以当我们获取一个被切面类监控管理的bean对象—UserDaoImp时,它实际上获取的是此对象的一个代理对象,而
在spring中对代理对象的处理有如下原则:(1)如果要代理的对象实现了接口,则会按照Proxy的方式来产生代理对象,这即是说产生的代理对象只能
是接口类型,比如起用上面注掉的代码就会报错,而且通过下面的打印语句我们也可以看出产生的是一个代理对象。(2)要代理的对象未实现接口,则按
cglib方式来产生代理对象。 另还要注意:要想spring的切面技术起作用,被管理的bean对象只能是通过spring容器获取的对象。比如这
里如果直接new出UseDaoImp对象,则new出的对象是不能被spring的切面类监控管理。
补充:测试被代理对象未实现接口时,spring切面技术的应用。
步骤一、修改切面类TheInterceptor切入点为如下内容:
@Pointcut("execution (* com.asm.dao.impl.*.*(..))")
说明:拦截impl包下的所有类所有方法
步骤二、在beans.xml中增加如下内容:
<bean id="userDaoImp2" class="com.asm.dao.impl.UserDaoImp2" />
步骤三、junit测试代码如下:
public void base2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
UserDaoImp2 udi2 = (UserDaoImp2) ctx.getBean("userDaoImp2");
System.out.println(udi2.getClass().getName());
System.out.println(udi2.getClass().getSuperclass().getName());
udi2.save();
}
说明:UseDaoImp2未实现任何接口,因此在spring中利用切面技术来管理此类使用的动态代理技术实质是cglib的动态代理方式,所以产生的代理对象实质是被代理对象的一个子类,通过上面的控制台打印语句可以看出。
小结
:
(1)声明aspect的切面类要纳入spring容器管理才能起作用。(2)被管理的bean实例要通过容器的getBeans方法获取。
(3)依据被管理的bean是否实现接口,spring采取两种方式来产生代理对象。(4)在xml文件中启用<aop:aspectj-
autoproxy/>
6.通知应用实例(基于注解)
在前一节,我们应用了前置通知和后置通知,除了这两个通知外,下面接着演示其它通知的应用。
(1)最终通知
在切面类TheInterceptor中增加如下代码即可:略去测试。
@After("anyMethod()")// 最终通知
public void after() {
System.out.println("最终通知");
}
(2)异常通知
为了演示此实例,我们在UseDaoImp中增加如下代码以抛出异常:
int i=1/0;在然后在切面类TheInterceptor中增加如下代码:
@AfterThrowing("anyMethod()") // 例外通知
public void AfterThrowing() {
System.out.println("例外通知");
}
当获取代理对象并调用save方法时会抛出异常,例外通知便会得以执行。
(3)环绕通知
@Around("anyMethod()") //环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入环绕");
//if(){ // 进行一些判断,再执行环绕
Object result = pjp.proceed();
//}
System.out.println("退出环绕");
return result;
}
注意的是方法的参数及抛出异常类型的固定写法(方法名可以是任意得),另在该方法中必须执行pjp.proceed()才能让环绕通知中的两处打印代码得
以执行。即是说要想环绕通知的拦截处理代码起作用必须调用pjp.proceed方法。 补充:环绕通知通常可以用来测试方法的执行时间
,在pjp.proceed前获取一个时间,在pjp.proceed方法后再获取一个时间。最后两个时间相减即可得方法执行时间。
(4)传递参数给通知
首先在UseDao接口中增加如下代码:
String add(String name);
然后再在UserDaoImp中实现此方法,代码如下:
public String add(String name) {
System.out.println("add method is called [ " + name+" ]");
return "添加成功";
}
需求:获取调用add方法传递的参数。操作步骤如下:在切面类增加如下代码:
@Before("anyMethod() && args(name)")// 前置通知,只针对UseDaoImp的add方法
public void beforeAdd(String name) {
System.out.println("前置通知:" + name);
}
说明:在前置通知的方法中有一个参数,然后再把此参数作为拦截条件(即是说拦截带有一个String类型参数的方法)。args的名字和beforeAdd方法参数名字相同。
测试代码:
- public void advieeTest() {
- ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
- UserDao ud=(UserDao) ctx.getBean("userDaoImp");
- ud.add("xxx");
- }
(5)获取方法的返回值
我们知道add方法有一个返回值,我们对此方法进行拦截并获取返回值,在切面类中增加如下代码:
@AfterReturning(pointcut = "anyMethod()", returning = "result")
// 后置通知,监听返回结果,针对UserDaoImp的getUsername()方法
public void afterReturningRes(String result) {
System.out.println("后置通知,返回结果:" + result);
}
说明:afterReturningRes方法的参数就是要返回的参数类型,returning标记的就是的结果,它的取值与该方法参数名相同。 测试代码同(4)。
(6)获取抛出的异常
切面类的增加如下代码:
@AfterThrowing(pointcut="anyMethod",throwing="e")
public void catchException(Exception e){
System.out.println("获取抛出的异常:"+e);
}
throwing的取值和方法的参数名相同,测试代码省略。
7. 通知应用实例(基于XML)
步骤一、复制TheInterceptorX类为TheInterceptorXML,并去掉所有注解。
步骤二、建立beansXML.xml配置文件,内容如下:
- <aop:aspectj-autoproxy /><!-- 开启切面编程功能 -->
- <bean id="userDaoImp" class="com.asm.dao.impl.UserDaoImp" />
- <bean id="aspectBean"
- class="com.asm.dao.impl.factory.TheInterceptorXML" />
- <aop:config>
- <aop:aspect id="asp" ref="aspectBean"> --声明一个切面类
- <aop:pointcut id="thecut" --声明一个切入点
- expression="execution(* com.asm.dao.impl.UserDaoImp.*(..))" />
- <aop:after-returning pointcut-ref="thecut" method="afterReturningRes" returning="result" />
- <aop:around pointcut-ref="thecut" method="around"/>
- <aop:after-throwing pointcut-ref="thecut"
method="catchException" throwing="e"/>
- <aop:after pointcut-ref="thecut" method="after" />
- <aop:before pointcut-ref="thecut" method="before" />
- </aop:aspect>
- </aop:config>
- 测试代码如下:
- public void advieeTest() {
- ApplicationContext ctx = new ClassPathXmlApplicationContext("beansXML.xml");
- UserDao ud=(UserDao) ctx.getBean("userDaoImp");
- ud.add("xxx");
- }
未解决问题:不能成功传参给前置通知。
8.解析
切入点表达式
1.格式
:execution(返回值 空格 方法选择
)。两部分组成,中间一定要有空格。
返回值:可以是*,说明拦截任何方法。Java.lang.String(全名),拦截返回值为String类型的方法。 常用的实例如下:
方法选择:包名[类名].*()。设定要拦截的方法签名。
9.总结
面向切面的常见应用(如权限
拦
截)、spring的aop依赖两种方式实现代理(依被代理的对象是否实现接口而定)、通知概念、基于注解与基于XML两种方式来配置切面、基本步骤(依
要拦截的方法来设定切入点,依据业务需求实现拦截通知代码,切面纳入spring容器管理,要被监控的类只能是通过Spring容器获取)、切入点的格
式。
发表评论
-
调试jdk中的源码,查看jdk局部变量
2013-06-15 23:30 1050调试jdk中的源码,查看jdk局部变量 2012-04 ... -
Eclipse快捷键 10个最有用的快捷键<转>
2013-04-11 23:28 1072Eclipse中10个最有用的快捷键组合 一个Eclip ... -
Lucene 3.6 中文分词、分页查询、高亮显示等
2012-12-09 23:35 18151、准备工作 下载lucene 3.6.1 : htt ... -
Maven实战(九)——打包的技巧(转)
2012-10-12 00:41 934“打包“这个词听起 ... -
基于Maven的web工程如何配置嵌入式Jetty Server开发调试环境(转)
2012-10-12 00:28 9231、首先在web工程的POM文件里添加依赖jar包如下: ... -
轻轻松松学Solr(1)--概述及安装[转]
2012-09-18 14:59 993概述 这段时间对企 ... -
分析Netty工作流程[转]
2012-09-04 19:02 886下面以Netty中Echo的例 ... -
让eclipse在ubuntu下面好看一点
2012-03-27 10:17 915<p> </p> <h1 cla ... -
zookeeper安装和应用场合(名字,配置,锁,队列,集群管理)[转]
2012-01-12 17:59 1647安装和配置详解 本文 ... -
Jakarta-Common-BeanUtils使用笔记[转]
2012-01-10 14:13 1153Jakarta-Common-BeanUtils ... -
一个关于Java Thread wait(),notify()的实用例【转】
2012-01-07 16:05 1020///// // ProducerConsume ... -
Java基础:Java中的 assert 关键字解析【转】
2012-01-06 19:50 1057J2SE 1.4在语言上提供了 ... -
一篇不错的讲解Java异常的文章(转载)----感觉很不错,读了以后很有启发[转]
2012-01-06 15:02 1263六种异常处理的陋习 ... -
如何解决HP QC(Quality Center)在Windows 7下不能工作的问题
2011-12-26 10:48 1577HP QC(Quantity Center) 是一款不错的测 ... -
JAVA读写文件,中文乱码 【转】
2011-12-19 23:43 2117最近在做HTML静态生成,需要从硬盘上把模版文件的内容读出来。 ... -
Java 6 JVM参数选项大全(中文版)【转】
2011-12-19 19:51 969Java 6 JVM参数选项大全(中文版) 作者 ... -
使用assembly plugin实现自定义打包【转】
2011-12-13 01:58 968在上一篇文章中,讨论到在对maven的机制不熟悉的情况下,为了 ... -
使用maven ant task实现非标准打包[转]
2011-12-13 01:56 1046maven很强大,但是总有些事情干起来不是得心应手,没有使用a ... -
Java日期转换SimpleDateFormat格式大全【转】
2011-12-08 20:22 131024小时制时间 显示: public clas ... -
使用Spring的表单标签库
2011-11-22 20:08 106913.9. 使用Spring的 ...
相关推荐
### Spring2-AOP入门实例教程知识点详解 #### 一、Spring框架...以上是基于《深入Spring2:轻量级J2EE开发框架原理与实践》一书中的知识点总结,希望能帮助初学者更好地理解和掌握Spring AOP的相关概念和使用方法。
### Spring入门学习资料知识点解析 #### 一、Spring框架简介 Spring框架是一个开源的轻量级企业级应用框架,主要用于简化Java EE应用的开发过程。它由Rod Johnson创建,最初是为了简化企业级应用的复杂性而设计的...
【标题】Spring02_工厂模式与Spring入门.zip 涉及的知识点主要围绕着软件设计模式中的工厂模式以及Spring框架的基础应用展开。这个压缩包内的资源,Spring02_工厂模式与Spring入门.wmv,可能是一个视频教程,旨在...
### Spring入门级教程知识点解析 #### 一、Spring框架概览 **1.1 核心概念** Spring框架的核心在于其轻量级容器(Container),它实现了控制反转(IoC)和面向切面编程(AOP)的概念,使得开发过程更为灵活且非侵入性。...
本资源摘要信息是关于轻松学JavaWeb开发之Spring框架入门PPT学习教案的知识点总结。 1. Spring概述:Spring是一种非常完整的技术,可以实现项目的开发。Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的...
### Spring知识总结 #### Spring框架概述 Spring框架是一款开源的Java平台应用框架,它主要针对企业级应用开发而设计,提供了全面的解决方案和技术支持。Spring框架的核心特性包括但不限于依赖注入(Dependency ...
【Spring入门笔记】主要介绍了Spring框架的基础知识,包括Spring的核心概念、Bean的配置与作用域、依赖注入、SpringAop和SpringJdbc,以及事务声明。以下是对这些知识点的详细说明: 1. **什么是Spring?** Spring...
- **定义**:Spring框架是一种轻量级的Java开发框架,它通过依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)等技术简化企业级应用的开发。 - **历史**:Spring框架最初由...
4. **数据访问**:Spring 的 DAO 模块简化了数据库访问操作,支持多种持久化技术。 #### 六、结语 Spring 框架凭借其简洁高效的设计理念,已经成为 Java 开发领域不可或缺的一部分。无论是初学者还是资深开发者,...
总结,Spring入门涉及的内容包括理解Spring的基本概念、创建配置文件、使用ApplicationContext接口以及掌握Setter依赖注入。通过学习这些基础知识,开发者能够开始构建基于Spring的应用程序,并逐渐探索更高级的特性...
### JAVA中spring入门教程知识点详解 #### 一、Spring框架简介 Spring是一个开源框架,它主要为Java平台提供了一个全面的编程和配置模型。Spring框架的设计目标是简化企业级应用程序的开发过程,使得开发者可以更加...
**Spring的AOP极简入门** 面向切面编程(Aspect-Oriented Programming,简称AOP)是Spring框架中的一项重要特性,它允许开发者定义“横切关注点”,这些关注点通常涉及日志、事务管理、安全性等通用功能,可以在不...
### Spring快速入门教程知识点解析 #### 一、Spring框架概览与优势 Spring框架是一个开源的轻量级Java EE框架,旨在简化企业级应用的开发。它提供了全面的解决方案,包括控制反转(Inversion of Control,IoC)、...
以下是 Spring Framework 4 参考文档中文版的知识点总结: 一、Spring Framework 概览 * Spring Framework 是一个轻量级的解决方案,是一站式构建企业级应用的一种选择。 * Spring Framework 支持声明式的事务管理...