转自
https://blog.csdn.net/jeffleo/article/details/54136904
Aspectj切入点语法定义
在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"
例如定义切入点表达式 execution(* com.sample.service.impl..*.*(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
-
execution(): 表达式主体。
-
第一个*号:表示返回类型,*号表示所有的类型。
-
包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
-
第二个*号:表示类名,*号表示所有的类。
-
*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
一、什么时候该用AOP:
假如业务模块一、二、三都需要日志记录,那么如果都在三个模块内写日志逻辑,那么会有两个问题:
- 打破模块的封装性
- 有很多重复代码
解决重复代码问题,可以通过封装日志逻辑为一个类,然后在各个模块需要的地方通过该类来试下日志功能,但是还是不能解决影响模块封装性的问题。
那么AOP就可以解决,它使用切面,动态地织入到各模块中(实际就是使用代理来管理模块对象),这样既解决了重复代码问题又不会影响模块的封装性
二、Spring AOP
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理,要在Spring 中使用AOP,还需要加入这两个jar包
- aopalliance.jar
- aspectjweaver.jar
★ Spring中 AOP中的两种代理:
- Java动态代理,这样就可以为任何接口实例创建代理了,默认使用
- 当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB
★ Spring AOP的使用步骤
- 定义具体业务逻辑模块(目标对象)
- 定义切面(即实现通知逻辑)
- 实现切面逻辑
三、基于Schema的Spring AOP实例
第一步 定义具体业务逻辑模块(目标对象)
两个业务逻辑模块都是基于接口
TestAOPDaoImpl .java
public class TestAOPDaoImpl implements TestAOPDao{
@Override
public void addUser() {
System.out.println("添加成功");
}
}
TestAOPServiceImpl.java
public class TestAOPServiceImpl implements TestAOPService{
@Autowired
private TestAOPDao testAOPDao;
@Override
public void addUser() {
testAOPDao.addUser();
}
}
第二步和第三步 定义切面(即实现通知逻辑)
JointPoint是连接点,aop创建代理后会返回一个连接点,然后在通知中可以通过该连接点实现我们的切面逻辑
日志切面
public class LogAdivice{
public void myBeforeAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——前置通知——" + methodname);
}
public void myAfterAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——后置通知——" + methodname);
}
/**
* 环绕通知将决定要不要执行连接点
* @throws Throwable
*/
public void myAroundAdivice(ProceedingJoinPoint point) throws Throwable{
System.out.println("环绕通知,执行代码前");
//选择执行
point.proceed();
System.out.println("环绕通知,执行代码后");
}
}
时间切面:
public class TimeAdvice {
public void timeBefore(){
System.out.println("beforeTime = " + System.currentTimeMillis());
}
public void timeAfter(){
System.out.println("afterTime = " + System.currentTimeMillis());
}
}
在applicationContext中配置切面:
<context:annotation-config/>
<bean id="testAOPDao" class="com.ssh.dao.impl.TestAOPDaoImpl"/>
<bean id="testAOPService" class="com.ssh.service.impl.TestAOPServiceImpl"/>
<bean id="logAdivice" class="com.ssh.adivice.LogAdivice"/>
<bean id="timeAdvice" class="com.ssh.adivice.TimeAdvice"/>
<aop:config>
<!-- 配置一个切面 -->
<aop:aspect id="logaop" ref="logAdivice" order="2">
<!-- 定义切入点,表示对service的所有方法都进行拦截 -->
<aop:pointcut expression="execution(* com.ssh.service.TestAOPService.*(..))" id="testpointcut"/>
<!-- 定义前置通知 -->
<aop:before method="myBeforeAdivice" pointcut-ref="testpointcut"/>
<!-- 定义后置通知 -->
<aop:after-returning method="myAfterAdivice" pointcut-ref="testpointcut"/>
<!-- 定义环绕通知 -->
<aop:around method="myAroundAdivice" pointcut-ref="testpointcut"/>
</aop:aspect>
<!-- 定义另一个切面 -->
<aop:aspect id="timeaop" ref="timeAdvice" order="1">
<!-- 定义切入点,表示对service的所有方法都进行拦截 -->
<aop:pointcut expression="execution(* com.ssh.service.TestAOPService.*(..))" id="testpointcut"/>
<!-- 定义前置通知 -->
<aop:before method="timeBefore" pointcut-ref="testpointcut"/>
<!-- 定义后置通知 -->
<aop:after-returning method="timeAfter" pointcut-ref="testpointcut"/>
</aop:aspect>
</aop:config>
当有多个切面时,Spring默认是按照切面定义的顺序来执行,也可以通过order属性来配置切面的执行属性,order=1 早于 order=2执行
测试结果
public class AOPTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
TestAOPService service = (TestAOPService) context.getBean("testAOPService");
service.addUser();
}
}
四、基于@AspectJ注解的AOP实现
第一步、定义具体业务逻辑模块(目标对象)
第一步和上面一样
TestAOPDaoImpl .java
public class TestAOPDaoImpl implements TestAOPDao{
@Override
public void addUser() {
System.out.println("添加成功");
}
}
TestAOPServiceImpl.java
public class TestAOPServiceImpl implements TestAOPService{
@Autowired
private TestAOPDao testAOPDao;
@Override
public void addUser() {
testAOPDao.addUser();
}
}
第二步和第三步、 定义切面(即实现通知逻辑)
重点是定义切入点
@Component
@Aspect
public class LogAdivice{
//定义一个方法作为切入点id
@Pointcut("execution(* com.ssh.service.TestAOPService.*(..))")
private void allMethod(){}
@Before("allMethod()")
public void myBeforeAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——前置通知——" + methodname);
}
@AfterReturning("allMethod()")
public void myAfterAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——后置通知——" + methodname);
}
/**
* 环绕通知将决定要不要执行连接点
* @throws Throwable
*/
@Around("allMethod()")
public void myAroundAdivice(ProceedingJoinPoint point) throws Throwable{
System.out.println("环绕通知,执行代码前");
//执行
point.proceed();
System.out.println("环绕通知,执行代码后");
}
}
在applicationContext的配置:
<!-- 打开自动扫描(隐式打开注解管理器) -->
<!-- <context:component-scan base-package="com.ssh"/> -->
<context:annotation-config/>
<bean id="testAOPDao" class="com.ssh.dao.impl.TestAOPDaoImpl"/>
<bean id="testAOPService" class="com.ssh.service.impl.TestAOPServiceImpl"/>
<bean id="logAdivice" class="com.ssh.adivice.LogAdivice"/>
<bean id="timeAdvice" class="com.ssh.adivice.TimeAdvice"/>
<!-- 打开aop注解管理器 -->
<aop:aspectj-autoproxy/>
五、Java代码使用AOP
public class TestControlFlowPointcut {
public static void main(String[] args) {
//只有TargetCaller中的方法才会被拦截
ControlFlowPointcut pointcut = new ControlFlowPointcut(TargetCaller.class);
BeforeAdvice beforeAdvice = new MethodBeforeAdvice() {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(method.getClass().getSimpleName() + ":" +
method.getName() + " - before logic ");
}
};
// Spring 中的 Aspect,装载pointcut和advice
PointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, beforeAdvice);
// Spring 基本织入器weaver
ProxyFactory weaver = new ProxyFactory();
weaver.setTarget(new TargetObject()); //指定代理目标对象
weaver.addAdvisor(advisor); //指定方面
Object proxy = weaver.getProxy();
//直接调用Targetobject的方法不会被拦截
((TargetObject)proxy).targetMethod();
//使用ControlFlowPointcut指定的类中的方法才会被拦截
TargetCaller caller = new TargetCaller();
caller.setTarget((TargetObject)proxy);
caller.callMethod();
}
}