打开Eclipse,新建Demo工程供编写测试程序使用。
spring aop学生信息 spring学校
spring aop学生信息 spring学校
spring aop学生信息 spring学校
导入编写测试程序使用到jar包。
注意:使用注解方式,必须引入aspectjrt和aspectjweer包。
下面万事俱备,只欠东风了。开始编写测试程序。编写测试使用的接口类EatInter。
编写接口实现类,实现接口中的方法。
编写aop的通知类,此部分可以看作为公共模块要实现的功能。
修改Spring的applicationContext.xml配置文件。可以看到使用注解方式时,配置文件是非常简洁的。只需要编写红框中的代码即可。
做完以上工作后,我们可以编写测试用的主类了。执行以下主类,查看测试结果。此时可以看到通知类中的公共代码也全部执行。
切面(Aspect) 一个关注点的模块化 这个关注点可能会横切多个对象 事务管理是J EE应用中一个关于横切关注点的很好的例子 在Spring AOP中 切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现
连接点(Joinpoint) 在程序执行过程中某个特定的点 比如某方法调用的时候或者处理异常的时候 在Spring AOP中 一个连接点 总是 代表一个方法的执行 通过声明一个 aspectj lang JoinPoint类型的参数可以使通知(A)的主体部分获得连接点信息
通知(A) 在切面的某个特定的连接点(Joinpoint)上执行的动作 通知有各种类型 其中包括 around before 和 after 等通知 通知的类型将在后面部分进行讨论 许多AOP框架 包括Spring 都是以做通知模型 并维护一个以连接点为中心的链
切入点(Pointcut) 匹配连接点(Joinpoint)的断言 通知和一个切入点表达式关联 并在满足这个切入点的连接点上运行(例如 当执行某个特定名称的方法时) 切入点表达式如何和连接点匹配是AOP的核心 Spring缺省使用AspectJ切入点语法
引入(Introduction) (也被称为内部类型声明(inter type declaration)) 声明额外的方法或者某个类型的字段 Spring允许引入新的接口(以及一个对应的实现)到任何被的对象 例如 你可以使用一个引入来使bean实现 IsModified 接口 以便简化缓存机制
目标对象(Target Object) 被一个或者多个切面(aspect)所通知(aise)的对象 也有人把它叫做 被通知(aised) 对象 既然Spring AOP是通过运行时实现的 这个对象永远是一个 被(proxied) 对象
AOP(AOP Proxy) AOP框架创建的对象 用来实现切面契约(aspect contract)(包括通知方法执行等功能) 在Spring中 AOP可以是JDK动态或者CGLIB 注意 Spring 新引入的基于模式(schema based)风格和@AspectJ注解风格的切面声明 对于使用这些风格的用户来说 的创建是透明的
织入(Weing) 把切面(aspect)连接到其它的应用程序类型或者对象上 并创建一个被通知(aised)的对象 这些可以在编译时(例如使用AspectJ编译器) 类加载时和运行时完成 Spring和其他纯Ja AOP框架一样 在运行时完成织入
通知的类型
前置通知(Before a) 在某连接点(join point)之前执行的通知 但这个通知不能阻止连接点前的执行(除非它抛出一个异常)
返回后通知(After returning a) 在某连接点(join point)正常完成后执行的通知 例如 一个方法没有抛出任何异常 正常返回
抛出异常后通知(After throwing a) 在方法抛出异常退出时执行的通知
后通知(After (finally) a) 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
lishixinzhi/Article/program/Ja/ky/201311/28064
原理:sping aop是可以通过预编译方式和运行期动态实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP 可以说也是这种目标的一种实现。
其原理的相关技术:
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。AOP(这里的AOP指的是面向切面编程思想,而不是Spring AOP)主要的的实现技术主要有Spring AOP和AspectJ。
AspectJ的底层技术是静态,即用一种AspectJ支持的特定语言编写切面,通过一个命令来编译,生成一个新的类,该类增强了业务类,这是在编译时增强,相对于下面说的运行时增强,编译时增强的性能更好。
Spring AOP采用的是动态,在运行期间对业务方法进行增强,所以不会生成新类,对于动态技术,Spring AOP提供了对JDK动态的支持以及CGLib的支持。
JDK动态只能为接口创建动态实例,而不能对类创建动态。需要获得被目标类的接口信息(应用Ja的反射技术),生成一个实现了接口的动态类(字节码),再通过反射机制获得动态类的构造函数,利用构造函数生成动态类的实例对象,在调用具体方法前调用invokeHandler方法来处理。
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现
在Spring中实现AOP根据版本不同,可以有大致四种配置方式。现简单列一下。在介绍Spring的AOP配置方式前,先要注意Spring中Aisor的概念。在Spring中Aisor是A和Pointcut的结合,但它还不是AOP概念上的Aspect。因为在Spring中Aisor还是Spring用来生成Aspect对象的一个原型,根据配置的不同,Spring可以只对某个类生成Aspect,也可以对所有的类生成Aspect。
1. 基于xml配置文件的配置方式
这种方式在2.0以后很少用了,原因是配置项过多,过于繁琐。但对于理解Spring AOP还是很有帮助的
1.1 定义通知
1.2 定义切点
要定义一个切点,可以选择使用正则表达式方式声明的切点或者AspectJ方式声明的切点。对正则表达式切点,使用Perl5RegexpModPointcut或JdkRegexpModPointcut(Ja
1.4以上版本,不需要Jaa ORO的支持了);对AspectJ切点,使用AspectJExpressPointcut
1.3 定义通知者
DefaultPointcutAisor是Spring提供的默认通知者,它需要提供通知和切点的引用。
Spring也提供了RegexpModPointcutAisor和AspectJExpressionPointcutAisor来对应两种声明切点的方式,不用再单独定义切点。
1.4 定义ProxyFactoryBean
这是一个BeanPostProcessor,所以Spring会自动识别并在bean的声明周期使用
2 利用2.0以后使用aop标签
3 利用Annotation
3.1 利用@Aspect将一个POJO类声明为一个切面。
3.2 定义切点
@Pointcut("execution( .perform(..))")
public void performance(){}
通过@Pointcut定义的切点的名字就是它所注解的方法的名字,因此例子中的切点名字是
performance()。这里声明的performance()方法实际圣只是一个标记,为@Pointcut提供附加的点,并不要求有实际意义。
3.3 定义通知
对要执行切面的方法,通过@Before("performance()"),@AfterReturning
("performance()")来定义通知。注意这里提供的切点名称,是performance(),而不是performance
如果对上面的两点不是很理解,也可以省略@Pointcut,而将AspectJ表达式直接定义在@Before等通知中,将上面的两步合为一步,如@Before("execution( .perform(..))")
3.4 通知Spring创建
这实际上相当于声明了一个AnnotationAwareAspectJAutoProxyCreator,从而根据@Pointcut声明的切点来自动匹配的bean实例
4 在Spring中结合进AspectJ
对于超出Spring AOP支持范围的,可以采用这种方式。只需要在Spring中配置AspectJ的Class实例时让Spring能够获得AspectJ类的实例就可以了,比如
1.新建一个Ja普通工程,并需导入spring-aop.jar包;
2.建UserInfo类:
packageaop.secure;publicclassUserInfo{privateStringuserName;privateStringpassword;publicUserInfo(StringuserName,Stringpassword){this.userName=userName;this.password=password;}publicStringgetPassword(){returnpassword;}publicStringgetUserName(){returnuserName;}}
3.建安全信息提示类SecureBean:
packageaop.secure;publicclassSecureBean{publicvoidwriteSecureMessage(){System.out.println("EverytimeIlearnsomingnewanditpushessomeoldstuffoutofmybrain.");}}
4.建切面类SecurityA实现org.springFramework.aop.ModBeforeA:
packageaop.secure;importja.lang.reflect.Mod;importorg.springframework.aop.ModBeforeA;publicclassSecurityAimplementsModBeforeA{privateSecurityMarsecurityMar;publicSecurityA(){this.securityMar=newSecurityMar();}publicvoidbefore(Modmod,Object[]args,Objecttarget)throwsThrowable{UserInfouser=securityMar.getLoggedOnUser();if(user==null){System.out.println("Nouserauthenticated.");thrownewSecurityException("Modname:"+mod.getName());}elseif("chigo".equals(user.getUserName())&&"chigo".equals(user.getPassword())){System.out.println("OKAY!");}else{System.out.println("Loggedinuseris:"+user.getUserName());thrownewSecurityException("User"+user.getUserName()+"isnotallowedAccesstomod"+mod.getName());}}}
5.建登陆与注销管理类SecurityMar:
packageaop.secure;publicclassSecurityMar{privatestaticThreadLocallocal=newThreadLocal();publicvoidlogin(StringuserName,Stringpassword){local.set(newUserInfo(userName,password));}publicvoidlogout(){local.set(null);}publicUserInfogetLoggedOnUser(){return(UserInfo)local.get();}}
6.后建测试类SecurityExample:
packageaop.secure;importorg.springframework.aop.framework.ProxyFactory;publicclassSecurityExample{privatestaticSecureBeangetSecureBean(){SecureBeanean=newSecureBean();SecurityAsa=newSecurityA();ProxyFactorypf=newProxyFactory();pf.setTarget(ean);pf.addA(sa);SecureBeanfactory=(SecureBean)pf.getProxy();returnfactory;}publicstaticvoidmain(String[]args){SecurityMarmgr=newSecurityMar();SecureBeanean=getSecureBean();mgr.login("chigo","chigo");ean.writeSecureMessage();mgr.logout();try{mgr.login("kkk","");ean.writeSecureMessage();}catch(SecurityExceptionex){System.out.println("Exceptioncaught:"+ex.getMessage());}finally{mgr.logout();}try{ean.writeSecureMessage();}catch(SecurityExceptionex){System.out.println("Exceptioncaught:"+ex.getMessage());}}}
SpringAOP是利用模式,在运行时生成一个目标对象的,并且使用代替目标对象,整个过程对使用者透明,使用者无法像使用目标对象一样使用对象,对象类型是目标对象所属类的子类或者接口实现,昌平IT培训认为这个子类也是在运行时动态生成,这个生成子类的过程使用作字节码技术,Spring框架中使用两种字节码技术:JDK动态和CGLIB,当目标类实现了接口时使用JDK动态,否则使用CGLIB。 AOP的实现包含下面几个步骤:
根据配置或注解解析切面。
生成AOP对象,给目标对象生成一个类以及类实例,根据解析出的切面,生成通知链设置到对象,在的回调中会执行通知链。
把AOP对象注册到容器中代替目标对象,当使用者向容器请求目标bean时,容器会返回对象。
下面对这几个步骤逐一的分析。
切面解析
在分析切面解析过程之前,首先先了解一下几个关键的接口,看下面的类图。 PointCut:描述切点,在进行切点匹配时,使用ClassFilter进行类匹配,ModMatcher进行执行方法匹配。
A:通知,AfterA后通知,BeforeA前通知,DynamicIntroductionA引用通知,环绕通知通过Interceptor实现。
Aisor:通知器,也就是切面,PointcutAisor切点通知器,IntroductionAisor引用通知器。
在创建AOP之前需要把相关的切面配置解析成上面类图中的接口子类的对象,对于ProxyFactoryBean来说,没有这个过程,因为这种方式下不能使用切点。
切面解析完成之后,把解析出的通知添加通知链中,AOP对象引用该通知链执行切面通知逻辑。对于aop标签方式和注解方式添加通知链这个动作的代码是类似的,解析切面这个过程有些异。
实现原理
前面在学习模式的时候,了解到模式分为动态和静态。现在我们就以模式为基础先实现我们自己的AOP框架,再来研究Spring的AOP的实现原理。
先以静态实现,静态关键是在对象和目标对象实现共同的接口,并且对象持有目标对象的引用。
公共接口代码:
1 public intece IHello {2 /3 业务方法4 @param str5 /6 void sayHello(String str);7 }
目标类代码:
1 public class Hello implements IHello{2 @Override3 public void sayHello(String str) {4 System.out.println("hello "+str);5 }6 7 }
类代码,我们给它添加日志记录功能,在方法开始前后执行特定的方法,是不是和AOP特别像呢?
public class ProxyHello implements IHello{
日志类代码:
1 public class Logger {2 public static void start(){3 System.out.println(new Date()+ " say hello start...");4 }5 6 public static void end(){7 System.out.println(new Date()+ " say hello end");8 }9 }
测试代码:
1 public class Test {2 public static void main(String[] args) {3 IHello hello = new ProxyHello(new Hello());//如果我们需要日志功能,则使用类4 //IHello hello = new Hello();//如果我们不需要日志功能则使用目标类5 hello.sayHello("明天");
这样我们就实现了一个简单的AOP,但是这样会存在一个问题:如果我们像Hello这样的类很多,那么,我们是不是要去写很多个HelloProxy这样的类呢。其实也是一种很麻烦的事。在jdk1.3以后,jdk跟我们提供了一个API ja.lang.reflect.InvocationHandler的类, 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事。下面我们就来实现动态的实现。
动态实现主要是实现InvocationHandler,并且将目标对象注入到对象中,利用反射机制来执行目标对象的方法。
接口实现与静态相同,类代码:
1 public class DynaProxyHello implements InvocationHandler{ 2 3 private Object target;//目标对象 4 / 5 通过反射来实例化目标对象 6 @param object 7 @return 8 / 9 public Object bind(Object object){10 this.target = object;11 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInteces(), this);12 }13 14 @Override15 public Object invoke(Object proxy, Mod mod, Object[] args)16 throws Throwable {17 Object result = null;18 Logger.start();//添加额外的方法19 //通过反射机制来运行目标对象的方法20 result = mod.invoke(this.target, args);21 Logger.end();22 return result;23 }24 25 }
测试类代码:
1 public class DynaTest {2 public static void main(String[] args) {3 IHello hello = (IHello) new DynaProxyHello().bind(new Hello());//如果我们需要日志功能,则使用类4 //IHello hello = new Hello();//如果我们不需要日志功能则使用目标类5 hello.sayHello("明天");6 }7 }
看完上面的代码可能和Spring AOP相比有一个问题,日志类只能在方法前后打印,但是AOP应该是可以在满足条件就可以执行,所有是否可以将DynaPoxyHello对象和日志作对象(Logger)解耦呢?
看下面代码实现,将将DynaPoxyHello对象和日志作对象(Logger)解耦:
我们要在被对象的方法前面或者后面去加上日志作代码(或者是其它作的代码),那么,我们可以抽象出一个接口,这个接口里就只有两个方法:一个是在被对象要执行方法之前执行的方法,我们取名为start,第二个方法就是在被对象执行方法之后执行的方法,我们取名为end。
Logger的接口:
1 public intece ILogger {2 void start(Mod mod);3 void end(Mod mod);4 }
Logger的接口实现:
1 public class DLogger implements ILogger{ 2 @Override 3 public void start(Mod mod) { 4 System.out.println(new Date()+ mod.getName() + " say hello start..."); 5 } 6 @Override 7 public void end(Mod mod) { 8 System.out.println(new Date()+ mod.getName() + " say hello end"); 9 }10 }
动态类:
1 public class DynaProxyHello1 implements InvocationHandler{ 2 //调用对象 3 private Object proxy; 4 //目标对象 5 private Object target; 6 7 public Object bind(Object target,Object proxy){ 8 this.target=target; 9 this.proxy=proxy;10 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInteces(), this);11 }12 13 14 @Override15 public Object invoke(Object proxy, Mod mod, Object[] args)16 throws Throwable {17 Object result = null;18 //反射得到作者的实例19 Class clazz = this.proxy.getClass();20 //反射得到作者的Start方法21 Mod start = clazz.getDeclaredMod("start", new Class[]{Mod.class});22 //反射执行start方法23 start.invoke(this.proxy, new Object[]{this.proxy.getClass()});24 //执行要处理对象的原本方法25 mod.invoke(this.target, args);26 //反射得到作者的end方法27 Mod end = clazz.getDeclaredMod("end", new Class[]{Mod.class});28 //反射执行end方法29 end.invoke(this.proxy, new Object[]{mod});30 return result;31 }32 33 }
测试代码:
1 public class DynaTest1 {2 public static void main(String[] args) {3 IHello hello = (IHello) new DynaProxyHello1().bind(new Hello(),new DLogger());//如果我们需要日志功能,则使用类4 //IHello hello = new Hello();//如果我们不需要日志功能则使用目标类5 hello.sayHello("明天");6 }7 }
通过上面例子,可以发现通过动态和发射技术,已经基本实现了AOP的功能,如果我们只需要在方法执行前打印日志,则可以不实现end()方法,这样就可以控制打印的时机了。如果我们想让指定的方法打印日志,我们只需要在invoke()方法中加一个对mod名字的判断,mod的名字可以写在xml文件中,这样我们就可以实现以配置文件进行解耦了,这样我们就实现了一个简单的spring aop框架。
版权声明:本文内容由互联。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发 836084111@qq.com 邮箱删除。