技术标签: aoparound xml配置 spring3.0 Spring AOP aopconfig
在软件中,有些行为是通用的。比如日志、安全和事务管理,他们有一个共同的特点,分布于应用中的多处,这种功能被称为横切关注点(cross-cutting concerns)。
DI(依赖注入)有助于应用对象之间的解耦,而AOP可以实现横切关注点与他们所影响的对象之间的解耦。
应用切面的常见范例:日志、声明式事务、安全和缓存。
下面涉及的内容包括Spring对切面的支持,包括如何把普通类声明为一个切面以及如何使用注解创建切面。
看完本文后,可以回过头来看下面这一段话。
面向切面编程在Spring AOP中有四种类型的调用:方法调用的之前、后、异常增加其他方法,方法调用的前和后调用其他方法,将方法中的参数传递给其他方法,将一个崭新的接口实现作为父类接口给其他方法所属的方法,这里要重点理解“其他方法”和你本来要调用的方法是分开编写的,即低耦合的。你在调用某个方法时,Sping AOP在其他类中就为这个方法额外做了其他事情。
这四种类型中的前三种都是容易理解和使用的,无非是本方法运行时,额外运行了其他方法,第四种需要注意的是,Spring AOP在不改变原有接口和实现的情况下,允许以将本接口强制转化为新接口类型的方式,然后通过Spring的DI(依赖注入)进行新方法的调用,详细看 8、通过页面引入新功能。
继承和委托是最常见的实现通用功能的面向对象技术。切面提供了取代继承和委托的另一种选择,而且在很多场景下更清晰简洁。
在使用面向切面编程时,我们仍然在一个地方定义通用功能,但是我们可以提供声明的方式定义这个功能以何种方式在何处应用,而无需修改受影响的类。
横切关注点可以被模块化为特殊的类,这些类被称为切面。
我用自己的话来通俗的说一下,切面是一个类,这个类的作用是完成对指定位置的指定动作的附属动作,指定位置首先有一个选择范围,可以是创建对象、方法调用、变量改变等等,这些选择范围就是连接点,你可以决定选取其中的一部分,这些被选取的部分就是切点,当然选择的过程在代码中体现,选好点后,一旦这些点被运行了,比如调用了一个类,切面里的类中的某个方法就会被调用,以完成别的功能,这个被调用的方法就是通知。
所以,可以简化为下面的关系:
切面:类。
切点:切面关注的指定代码的动作。(尽管可以是创建对象,方法调用,变量改变,但是Spring 3.0中的Spring AOP仅支持方法调用,体现在切点就是某个方法的类路径+)
连接点:可以作为切点的代码动作。
通知:方法,切点处额外多出的方法。
切面的工作被称为通知。
通知定义了切面的什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。
它应该应用于某个方法被调用之前?之后?之前和之后?还是只在方法抛出异常时?
通知的5种类型
·Before-在方法被调用之前调用通知。
·After-在方法被调用之后调用通知,无论方法是否执行成功。
·After-returning-在方法成功执行之后调用通知。
·After-throwing-在方法抛出异常后调用通知。
·Around-方法被调用之前和调用之后各执行一次自定义的行为,通知把被通知方法“包裹”起来了。
连接点是在应用执行过程中能够插入切面的一个点,准确的说是程序运行中的某种时机,比如调用方法时,抛出异常时、甚至是修改一个字段时。
切面代码可以利用这些点(时机)插入到应用的正常流程之中,并添加新的行为。
一个切面(什么是切面?)仅仅需要通知有限的连接点而不是全部的连接点。切点定义了连接点中的那些是通知需要产生作用的。
切点会匹配通知所要织入的一个或者多个连接点。
我们通常使用明确的类和方法名称来指定这些切点。
通俗来说,切点是相对于不同通知而言的有效连接点的集合。
切面是通知和切点的集合,通知决定做什么?何时?切点决定在那些地方进行通知。比如,我们要在火车站的售票窗口或者代售点(切点)买票,时间是早晨8点到下午5点(通知-什么时候),动作是付钱买票(通知-做什么)。
引入是一个过程的描述,指我们向现有的类添加新方法或属性。我们可以把一个类作为通知,引入到被通知的类中,从而在不改变被通知的类的情况下改变这个类。
切面在指定的连接点(即通知在切点通知被通知的方法,通知被引入了被通知的类)被织入到目标对象中,括号中一直说被通知类增加了方法,这种增加的方法就是织入,说到底,织入是为被通知类创建了代理类,在代理类中增加了方法。从表面看来,被代理类增加了方法,即织入。
织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。
在目标对象的多个点可以进行织入。
通过在代理类中包裹切面,Spring在运行期将切面织入到Spring管理的Bean中。
编译期——切面在目标类编译时被织入。
·这种方式需要特殊的编译器。
·AspectJ的织入编译器就是以这种方式织入切面的。
类加载期——切面在目标类加载到JVM时被织入。
·这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。
·AspectJ 5的LTW(load-time weaving)就支持以这种方式织入切面。
运行期——切面在应用运行期间的某个时段被织入。
·一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。
·Spring AOP 就是在运行期,为目标对象动态创建一个代理对象来实现织入的。当拦截到方法调用时。在调用目标Bean之前,代理会执行切面逻辑。直到应用需要被代理的Bean时,Spring才创建代理对象。
创建切点来定义切面织入的连接点是AOP框架的基本功能。
·AspectJ (http://eclipse.org/aspectj);
·JBoss AOP (http://www.jboss.org/jbossaop);
·Spring AOP (http://www.springframework.org)。
前3种都是Spring基于代理的AOP变体,因此,Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单方法拦截的范畴(如构造器或属性拦截),那么应该考虑在AspectJ里实现切面,利用Spring的DI(依赖注入)把Spring Bean注入到AspectJ切面中:
·基于代理的经典AOP;
·@AspectJ注解驱动的切面;
·纯POJO切面;
·注入式AspectJ切面(适合Spring各版本)。
经典Spring AOP使用ProxyFactoryBean,由于有更简单的方法,这里不再做介绍。
AspectJ和Jboss除了方法切点,还提供了字段和构造器接入点。所以,单单使用Spring我们无法构建细粒度的通知,也无法使用构造器连接点。对于这种情况,我们可以利用Aspect来协助Spring AOP。
我们使用切点来选择连接点,然后是不同的通知匹配不同的切点。在Spring AOP中,需要使用AspectJ的切点表达式来定义切点。
arg() 限制连接点匹配参数为指定类型的执行方法
@args() 限制连接点匹配参数由指定注解标注的执行方法
execution() 用于匹配是连接点的执行方法
this() 限制连接点匹配AOP代理的Bean引用为指定类型的类
target() 限制连接点匹配目标对象为指定类型的类
@target() 限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解
within() 限制连接点匹配指定的类型
@within() 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时,方法定义在由指定的注解所标注的类里)
@annotation 限制匹配带有指定注解连接点
只有execution切点指示器是唯一的执行匹配,而其他的切点指示器都是用于限制匹配的。
注意:所写的路径都是接口类的路径,切点是在接口类中而不是具体的实现类中的。
不关注返回类型,不关注入参类型,在方法执行的时候引入切点
execution(* com.springination.springidol.Instrument.play(..))
增加限制-并且限定了切点的包路径(后者是多余的吗?)
execution(* com.springination.springidol.Instrument.play(..))
and within(com.springinaction.springidol.*)
在spring2.5之后,可以通过id来限定bean
execution(* com.springination.springidol.Instrument.play(..))
and within(com.springinaction.springidol.*)
and bean(eddie)
当然也可以使用反向操作
execution(* com.springination.springidol.Instrument.play(..))
and within(com.springinaction.springidol.*)
and bean(eddie)
and !bean(eddie2)
AOP配置元素 描述
<aop:advisor> 定义AOP通知器
<aop:after> 定义AOP后置通知(不管被通知的方法是否执行成功)
<aop:after-returning> 定义AOP after-returning通知
<aop:after-throwing> 定义after-throwing通知
<aop:around> 定义AOP环绕通知
<aop:aspect> 定义切面
<aop:aspect-autoproxy> 启用@AspectJ注解驱动的切面
<aop:before> 定义AOP前置通知
<aop:config> 顶层的AOP配置元素。大多数的<AOP:*>元素,必须包含在 <aop:config>元素内
<aop:declare-parents> 为被通知的对象引入额外的接口,并透明地实现
<aop:pointcut> 定义切点
首先需要搭建起Spring 的环境,如果还没有自己的spring环境,可以参考文章
http://blog.csdn.net/bestcxx/article/details/52488620
http://blog.csdn.net/bestcxx/article/details/52620482
Spring AOP需要四个额外的jar包,aopalliance-1.0.jar,aspectjweaver-1.8.9.jar,aspectjrt-1.8.9.jar,aspectjtools-1.8.9.jar
下载地址:http://download.csdn.net/detail/bestcxx/9654632
(如果你使用了maven那么下载jar包会是一件很简单的事情,
搭建起你的maven环境,http://blog.csdn.net/bestcxx/article/details/52126907
搜索你需要的jar包的dependency语句添加到pom.xml文件,自动下载到本地maven库
http://mvnrepository.com/artifact/org.apache.servicemix.bundles/org.apache.servicemix.bundles.aspectj/1.8.9_1)
内容
package stu.bestcxx.springtest.springaop;
/**
* Spring AOP测试 接口
* @author WuJieJecket
*
*/
public interface AopPerformer {
public void perform();
}
内容
package stu.bestcxx.springtest.springaopimpl;
import stu.bestcxx.springtest.springaop.AopPerformer;
/**
* Spring AOP测试 bean
* @author WuJieJecket
*/
public class AopPerformerImpl implements AopPerformer {
@Override
public void perform() {
// TODO Auto-generated method stub
System.out.println("演员表演了歌唱");
}
}
内容
package stu.bestcxx.springtest.springaopimpl;
/**
* Spring AOP测试
* 被织入的通知
*
* 把观众的动作作为织入的内容
*
* @author WuJieJecket
*
*/
public class Audience {
/**
* 表演之前
*/
public void takeSeats(){
System.out.println("SpringAOP方法:观众们坐在了自己的座位上。");
}
/**
* 表演之前
*/
public void turnOffCellPhones(){
System.out.println("SpringAOP方法:观众们把手机调整为关机状态。");
}
/**
* 表演之后
*/
public void applaud(){
System.out.println("SpringAOP方法:全体观众起身鼓掌");
}
/**
* 表演失败
*/
public void demandRefund(){
System.out.println("SpringAOP方法:全体观众倒喝彩");
}
}
内容
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config/>
<!-- 表演者 -->
<bean id="aopPerformerImpl" class="stu.bestcxx.springtest.springaopimpl.AopPerformerImpl"/>
<!-- 观众 -->
<bean id="audience" class="stu.bestcxx.springtest.springaopimpl.Audience"></bean>
<!-- 为接口类设置切点 -->
<aop:config>
<aop:aspect ref="audience">
<!-- 之前 -->
<aop:before pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="takeSeats"/>
<!-- 之前 -->
<aop:before pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="turnOffCellPhones"/>
<!-- 之后 -->
<aop:after-returning pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="applaud"/>
<!-- 之后 -->
<aop:after-throwing pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="demandRefund"/>
</aop:aspect>
</aop:config>
</beans>
内容
package stu.bestcxx.springtest.springaopimpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.springaop.AopPerformer;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextAop.xml"})
@TransactionConfiguration(transactionManager="defaultTransactionManager",defaultRollback=false)
public class AopPerformerImplTest {
@Autowired
private AopPerformer aopPerformer;
@Test
public void testperform(){
aopPerformer.perform();
}
}
运行结果:
为了直观比较,我们新增一个观众
Audience2.java
package stu.bestcxx.springtest.springaopimpl;
/**
* Spring AOP测试
* 被织入的通知
*
* 把观众的动作作为织入的内容
*
* @author WuJieJecket
*
*/
public class Audience2 {
/**
* 表演之前
*/
public void takeSeats(){
System.out.println("SpringAOP2方法:观众们坐在了自己的座位上。");
}
/**
* 表演之前
*/
public void turnOffCellPhones(){
System.out.println("SpringAOP2方法:观众们把手机调整为关机状态。");
}
/**
* 表演之后
*/
public void applaud(){
System.out.println("SpringAOP2方法:全体观众起身鼓掌");
}
/**
* 表演失败
*/
public void demandRefund(){
System.out.println("SpringAOP2方法:全体观众倒喝彩");
}
}
applicationContextAop.xml内容扩增为:
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config/>
<!-- 表演者 -->
<bean id="aopPerformerImpl" class="stu.bestcxx.springtest.springaopimpl.AopPerformerImpl"/>
<!-- 观众 -->
<bean id="audience" class="stu.bestcxx.springtest.springaopimpl.Audience"></bean>
<!-- 观众2 -->
<bean id="audience2" class="stu.bestcxx.springtest.springaopimpl.Audience2"></bean>
<!-- 为接口类设置切点 -->
<aop:config>
<!-- 分散式配置切点 -->
<aop:aspect ref="audience">
<!-- 之前 -->
<aop:before pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="takeSeats"/>
<!-- 之前 -->
<aop:before pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="turnOffCellPhones"/>
<!-- 之后 -->
<aop:after-returning pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="applaud"/>
<!-- 之后 -->
<aop:after-throwing pointcut="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" method="demandRefund"/>
</aop:aspect>
<!-- 集中式配置切点 -->
<aop:aspect ref="audience2">
<!-- 声明切点 -->
<aop:pointcut expression="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" id="perform"/>
<!-- 之前 -->
<aop:before pointcut-ref="perform" method="takeSeats"/>
<!-- 之前 -->
<aop:before pointcut-ref="perform" method="turnOffCellPhones"/>
<!-- 之后 -->
<aop:after-returning pointcut-ref="perform" method="applaud"/>
<!-- 之后 -->
<aop:after-throwing pointcut-ref="perform" method="demandRefund"/>
</aop:aspect>
</aop:config>
</beans>
测试方法不变,AopPerformerImplTest.java
运行结果为
有时我们需要在前置通知和后置通知之间共享信息。声明环绕提供了这种服务,其把被织入切点的类的方法的执行代理到一个切点类中,在之前和之后运行所需的功能代码,说到底,前置通知和后置通知的功能代码被写在了同一个方法中。
Public * method(* 被代理方法){
/*代码1*/
被代理方法
/*代码2*/
}
6.1.1编写切点类AudienceWatch.java
内容
package stu.bestcxx.springtest.springaopimpl;
import org.aspectj.lang.ProceedingJoinPoint;
public class AudienceWatch {
/**
* ProceedingJoinPoint让我们在通知的方法里调用被通知的方法
* @param joinpoint
*/
public void watchPerforance(ProceedingJoinPoint joinpoint){
try {
System.out.println("观众们坐在了自己的座位上。");
System.out.println("观众们把手机调整为了静音状态。");
long start=System.currentTimeMillis();
//执行被通知的方法,如果没有该语句,通知将不会进行
for(int i=0;i<=1000;i++){
joinpoint.proceed();
}
long end=System.currentTimeMillis();
System.out.println("观众报以热烈的掌声");
System.out.println("表演者花费的时间为 "+(end-start)+"milliseconds");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
6.1.2 Spring XML配置文件 applicationContextAopAround.xml
内容
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config/>
<!-- 表演者 -->
<bean id="aopPerformerImpl" class="stu.bestcxx.springtest.springaopimpl.AopPerformerImpl"/>
<!-- 观众 -->
<bean id="audienceWatch" class="stu.bestcxx.springtest.springaopimpl.AudienceWatch"></bean>
<!-- 为接口类设置切点 -->
<aop:config>
<!-- 集中式配置切点 -->
<aop:aspect ref="audienceWatch">
<!-- 声明切点 -->
<aop:pointcut expression="execution(* stu.bestcxx.springtest.springaop.AopPerformer.perform(..))" id="perform"/>
<!-- 之前 -->
<aop:around pointcut-ref="perform" method="watchPerforance"/>
</aop:aspect>
</aop:config>
</beans>
6.1.3 测试类AudienceWatchTest.java
package stu.bestcxx.springtest.springaopimpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.springaop.AopPerformer;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextAopAround.xml"})
@TransactionConfiguration(transactionManager="defaultTransactionManager",defaultRollback=false)
public class AudienceWatchTest {
@Autowired
private AopPerformer aopPerformer;
@Test
public void testperform(){
aopPerformer.perform();
}
}
测试结果
观众们坐在了自己的座位上。
观众们把手机调整为了静音状态。
演员表演了歌唱
...
演员表演了歌唱
观众报以热烈的掌声
表演者花费的时间为 25milliseconds
我们将在切点方法中获取被通知方法中的一个入参。
两个接口类,两个实现类,一个实现切点方法,一个实现被通知的方法。
package stu.bestcxx.springtest.springaopargs;
/**
* MindReader完成两件事情,截听志愿者内心感应和显示他们在想什么。
* 可以看另一个志愿者类,其思考需要被读心者拦截
* 类似于,为切点传递了被通知类的方法中的参数
* @author WuJieJecket
*
*/
public interface MindReader {
public void interceptThoughts(String thoughts);
public String getThoughts();
}
package stu.bestcxx.springtest.springaopargs;
/**
* 思考者,其思考的内容将被切点类读心者截取
* @author WuJieJecket
*
*/
public interface Thinker {
public void thinkOfSomething(String thoughts);
}
package stu.bestcxx.springtest.springaopargsimpl;
import stu.bestcxx.springtest.springaopargs.MindReader;
public class Magician implements MindReader {
private String thoughts;
@Override
public void interceptThoughts(String thoughts) {
// TODO Auto-generated method stub
System.out.println("AOP args 截取志愿者思考着的想法。");
this.thoughts=thoughts;
}
@Override
public String getThoughts() {
// TODO Auto-generated method stub
return thoughts;
}
}
package stu.bestcxx.springtest.springaopargsimpl;
import stu.bestcxx.springtest.springaopargs.Thinker;
public class Volunteer implements Thinker {
public String thoughts;
@Override
public void thinkOfSomething(String thoughts) {
// TODO Auto-generated method stub
this.thoughts=thoughts;
}
public String getThoughts() {
return thoughts;
}
}
内容:
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config/>
<bean id="magician" class="stu.bestcxx.springtest.springaopargsimpl.Magician"/>
<bean id="volunteer" class="stu.bestcxx.springtest.springaopargsimpl.Volunteer"/>
<aop:config>
<!-- 定义切点 -->
<aop:aspect ref="magician">
<aop:pointcut id="thinking" expression="execution(* stu.bestcxx.springtest.springaopargs.Thinker.thinkOfSomething(*)) and args(thoughts)"/>
<aop:before pointcut-ref="thinking"
method="interceptThoughts"
arg-names="thoughts"/>
</aop:aspect>
</aop:config>
</beans>
内容:
package stu.bestcxx.springtest.springaopargsimpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.springaopargs.MindReader;
import stu.bestcxx.springtest.springaopargs.Thinker;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextAopargs.xml"})
@TransactionConfiguration(transactionManager="defaultTransactionManager",defaultRollback=false)
public class VolunteerTest {
@Autowired
private Thinker thinker;
@Autowired
private MindReader mindReader;
@Test
public void testVolunteer(){
thinker.thinkOfSomething("我是志愿者,我在想今晚吃什么?还是回家吃面条吧~");
System.out.println("切点截取的信息为: "+mindReader.getThoughts());
}
}
切面可以为Spring Bean添加新方法。切面只是实现了他们所包装Bean的相同接口的代理。如果除了实现这些接口,代理还能发布新接口的话,切面就可以为Spring Bean添加新方法。
事实上,在进行这部分实验的时候我犯了一个错误,当我把一个新的接口类以及其实现作为新功能加到被通知类中时,我认为新类的实现和被通知类的实现是两个不同type的bean(你应该记得byType和@Autowired是一个含义,但是不加其他限制条件的情况下仅允许一个接口类被一个bean实现),然而,事实上,在这样应用@Autowired后报错了,而且新加入的方法的调用是需要显式调用的。
下面看可以正常运行的例子吧
在刚才目录结构的基础上,新增两个类,一个是新功能的接口类ThinkerAdd.java,一个是该接口的实现类ThinkerAddImpl.java,但是最终,这个实现类会被认为是被通知接口类的一个实现类。
package stu.bestcxx.springtest.springaopargs;
public interface ThinkerAdd {
public void getThisMethod();
}
package stu.bestcxx.springtest.springaopargsimpl;
import stu.bestcxx.springtest.springaopargs.ThinkerAdd;
public class ThinkerAddImpl implements ThinkerAdd {
@Override
public void getThisMethod() {
// TODO Auto-generated method stub
System.out.println("这是切点类为被通知的类增加的方法的打印输出。");
}
}
内容
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config/>
<bean id="volunteer" class="stu.bestcxx.springtest.springaopargsimpl.Volunteer"/>
<bean id="thinkeraddimpl" class="stu.bestcxx.springtest.springaopargsimpl.ThinkerAddImpl"/>
<!-- delegate-ref="thinkeraddimpl" 可以更换为
default-impl="stu.bestcxx.springtest.springaopargs.ThinkerAdd.ThinkerAddImpl"
区别在于,如果以单独的bean来声明,其本身也可以增加切面
-->
<aop:config>
<aop:aspect>
<aop:declare-parents
types-matching="stu.bestcxx.springtest.springaopargs.Thinker+"
implement-interface="stu.bestcxx.springtest.springaopargs.ThinkerAdd"
delegate-ref="thinkeraddimpl"
/>
</aop:aspect>
</aop:config>
</beans>
package stu.bestcxx.springtest.springaopargsimpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.springaopargs.Thinker;
import stu.bestcxx.springtest.springaopargs.ThinkerAdd;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextAopargsadd.xml"})
@TransactionConfiguration(transactionManager="defaultTransactionManager",defaultRollback=false)
public class VolunteerAddTest {
@Autowired
private Thinker thinker;
@Test
public void testVolunteer(){
//被代理的接口的方法的实现
thinker.thinkOfSomething("我是被通知的接口类的实现,切面会为我所属的对象增加一个父类的接口,然后通过强制转化,我可以直接使用这个新接口的方法,就这样我具备了新的功能,而新接口本身也可以被其他切面处理");
//强制转化来实现切面中增加代理接口的实现,我们这样为thinker增加了方法
((ThinkerAdd)thinker).getThisMethod();
}
}
事实上,这个时候,由于我们的志愿者方法只是完成了一个内部赋值的操作,测试不是很明显,所以我们对类Volunteer.java做了微调,只是加了一句System.out.println();输出,以证明被通知的类的确运行了。为了不使布局混乱,我这把这个java文件贴出来
内容
package stu.bestcxx.springtest.springaopargsimpl;
import stu.bestcxx.springtest.springaopargs.Thinker;
public class Volunteer implements Thinker {
public String thoughts;
@Override
public void thinkOfSomething(String thoughts) {
// TODO Auto-generated method stub
this.thoughts=thoughts;
System.out.println("被通知的类-志愿者说:"+thoughts);
}
public String getThoughts() {
return thoughts;
}
}
Spring3.0 学习-AOP面向切面编程_Spring AOP的XML配置模式 结
junit 测试方法控制回滚,需要继承
public class TClassServiceTest extends AbstractTransactionalJUnit4SpringContextTests{
表达式例子如下:
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
文章浏览阅读363次。LF:Line Feed,即换行("\n"),Linux系统环境下的换行方式。CRLF:Carriage Return Line Feed,即回车换行("\r\n"),Windows系统环境下的换行方式。_crlf与lf区别
文章浏览阅读1.4k次。按下相应的快捷键后,Eclipse将自动根据您的代码格式化偏好设置对代码进行格式化。请确保已经选择和配置了适合您编码风格的格式化规则和偏好设置。您可以在Eclipse的首选项(Preferences)> Java > 代码风格 > 格式化 中进行相关配置。请注意,这些快捷键在默认情况下适用于Java代码,但可以在其他编程语言中进行使用。如果您正在使用不同的编程语言,请查看相应语言的格式化快捷键配置。_eclipse格式化代码快捷键
文章浏览阅读1.4w次,点赞2次,收藏19次。苹果手机可以直接在线预览PDF文件,而安卓手机不行,必须得下载(如图),所以需要解决一下1.准备所需js文件(1)js下载地址https://mozilla.github.io/pdf.js/(2)下载步骤 ①:打开网址后,点击图中的“Download”②:选择版本,直接下载即可2.在项目中导入相关JS文件(如图)3.编写代码(1)引入js文件..._pdf文件手机在线浏览
文章浏览阅读1.8k次,点赞8次,收藏36次。【OTFS与信号处理:论文阅读】基于OTFS调制的迭代信道估计与检测算法 Iterative Channel Estimationand Data Detection Algorithm For OTFS Modulation_otfs叠加导频
文章浏览阅读886次。geometry字段存在oracle、postgresql等库体定义模型不一样;通过kettle默认自定义导出sql后,放到异构库里执行会报错。例如postgresql未加处理时,导出的st_geometry的二进制格式;在到oracle库中执行就会报错。为了执行放跨库同步方便,需要将geometry字段生成sql时,转换成对方wkt转geometry方法与wkt的拼接字符串形态;1、导出pg支持的geometry对应sql如果下是将postgresql中的geometry生成postgresql支_kettle geometry
文章浏览阅读3.7k次。前言在工作当中,可能经常会遇到比如数据保留两位小数显示,去除后面多余0,按指定格式输出数据这种需求,有时隔得时间久了也难免会忘记,所以就稍作总结方便今后查看,同时最后提供一个工具类方便今后使用。NumberFormatNumberFormat 是所有数值格式的抽象基类。此类提供格式化和解析数值的接口。NumberFormat 还提供了一些方法来确定哪些语言环境具有数值格式,以及它们的名..._numberformat
文章浏览阅读533次,点赞5次,收藏7次。一、什么是Mybatis-plusMybatis的出现 是为了简化JDBC的复杂操作,而Mybatis-plus的出现则是为了简化Mybatis的操作,它不会改变mybatis原有的东西,只会在原有的基础上增加功能,可以说,它是非入侵式的,蕴含了AOP的思想二、Mybatis-plus的特点无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作强大的 CRUD 操作:内置通用 Mapper、通用 Service_mybatisplus中的->作用是什么
文章浏览阅读130次。一、亨元模式享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接..._是亨元模式还是享元模式
文章浏览阅读251次。第1篇 鼠标与键盘控制篇 第1章 鼠标操作控制 21.1 获取鼠标信息 3 实例001 获取鼠标双击时间间隔 3 实例002 获取光标闪烁的频率 4 实例003 获取鼠标键数 4 实例004 显示鼠标的等待光标 5 实例005 获得鼠标在窗体上的位置 6 实例006 记录鼠标行为 7 实例007 通过截取系统消息判断鼠标的单击键 8..._c#开发实战1200例(第i卷、第ii卷)2本合售(2本都有光盘) 王小
文章浏览阅读1.2k次,点赞2次,收藏5次。计算机网络实验/路由器基本配置和静态路由/实验报告_路由器telnet登陆与静态路由配置
文章浏览阅读1w次,点赞28次,收藏101次。推荐:cs224n_exercise1. 作业1[NLP] cs224n-2019 Assignment 1 Exploring Word Vectors2019-CS224n-Assignment1_cs224n 2018-19: homework 4
文章浏览阅读450次。1 .微信小程序开发官方文档 2 .CSDN博文精选 微信小程序全方位解析 3 .一名Android开发者的微信小程序填坑之路(1) 4 .微信直播在小程序上使用 5 .Android 实现微信,QQ的程序前后台切换:back键切换后台;点击通知栏恢复前台。 6 .「微信小程序」有哪些冲击与机会? 7 .微信小程序简单教程 8 .微信小程序入门教程+案例demo 9 .目前为止最全_爬虫 阿里云 ip代理