Spring基础入门-aop(纯注解annotation)
项目目录结构
1.导入相应的jar包,创建相应的类
IUser.java
package com.zh.aop;
public interface IUser {
/**
* 添加操作
*/
public abstract void add();
/**
* 删除操作
*/
public abstract void delete();
/**
* 更新操作
*/
public abstract void update();
/**
* 查询操作
*/
public abstract void query();
}
User.java
package com.zh.aop;
import org.springframework.stereotype.Service;
@Service("getUser")
public class User implements IUser {
@Override
public void add() {
System.out.println("User do add");
}
@Override
public void delete() {
System.out.println("User do delete");
}
@Override
public void update() {
System.out.println("User do update");
}
@Override
public void query() {
System.out.println("User do query");
}
}
Manager.java
package com.zh.aop;
import org.springframework.stereotype.Service;
@Service("getManager")
public class Manager implements IUser {
@Override
public void add() {
System.out.println("Manager do add");
}
@Override
public void delete() {
System.out.println("Manager do delete");
}
@Override
public void update() {
System.out.println("Manager do update");
}
@Override
public void query() {
System.out.println("Manager do query");
}
}
Logger.java
package com.zh.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component("getQieMian")
@Aspect //标识配置切面
public class Logger {
/**
* 配置切点
*/
@Pointcut("execution(* com.zh.aop..*(..))")
public void poitCut() {}
/**
* 前置通知,在切入点执行前执行
*/
@Before(value = "poitCut()")
public void before() {
System.out.println("before(前置通知)");
}
/**
* 后置通知,在切入点正常执行后
*/
@AfterReturning(value = "poitCut()")
public void after_returning() {
System.out.println("after-returning(后置通知)");
}
/**
* 最终通知,无论切入点能不能正常执行,它都会
*/
@After(value = "poitCut()")
public void after() {
System.out.println("after(最终通知)");
}
/**
* 异常通知,在切入点执行产生异常后执行
*/
@AfterThrowing(value = "poitCut()")
public void after_throwing() {
System.out.println("after-throwing(异常通知)");
}
/**
* 环绕通知:
* 它是spring框架提供的一种可以在代码中手动控制通知方法什么时候执行的方式
* 环绕通知问题:
* 当我们配置了<aop:around method="around()" pointcut-ref="doAop"/>之后
* around方法执行了,但是切入点方法没有执行
* 由动态代理可以知道,环绕通知指的是invoke方法,并且里面有明确的切入点方法调用。而现在我们的环绕通知没有明确切入点方法调用
* 但Spring为我们提供了一个接口:ProceedingJoinPoint。该接口可以作为环绕通知的方法参数使用
* 在程序运行时,spring框架为我们提供该接口的实现类,供我们使用
* 该接口中有一个proceed方法,它相当于method.invoke,就是明确调用业务核心方法(切入点方法)
*/
@Around(value = "poitCut()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("around(环绕通知开始)");
Object o = null;
try {
//写这里相当于前置通知
System.out.println("around(前置通知)");
o = pjp.proceed();
//写这里相当于后置通知
System.out.println("around(后置通知)");
} catch (Throwable e) {
//写这里相当于异常通知
System.out.println("around(异常通知)");
e.printStackTrace();
}finally {
//写这里相当于最终通知
System.out.println("around(最终通知)");
}
System.out.println("around(环绕通知结束)");
return o;
}
}
Test_aop.java
package com.zh.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test_aop {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:beans.xml");
//aop的底层原理是动态代理实现增强,有两种实现,一种是接口代理,另一种是cglib子类代理
//所以这里必须要使用接口,而且必须使用接口来接收
//获得普通用户对象,使用接口接收
IUser user1 = ac.getBean("getUser",IUser.class);
user1.add();
System.out.println("-------------------");
user1.delete();
System.out.println("-------------------");
user1.update();
System.out.println("-------------------");
user1.query();
System.out.println("###################");
//获得管理员对象,使用接口接收
IUser user2 = (IUser) ac.getBean("getManager");
user2.add();
System.out.println("-------------------");
user2.delete();
System.out.println("-------------------");
user2.update();
System.out.println("-------------------");
user2.query();
}
}
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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring容器创建时要扫描的包 -->
<context:component-scan base-package="com.zh.aop"></context:component-scan>
<!-- 开启spring对注解aop的支持 -->
<aop:aspectj-autoproxy/>
</beans>
2.运行效果
《提示》
1.使用具体哪个通知,就留哪个通知(其他内容全部注释掉)就可以运行出效果。
2.肯定有的人觉得after应该为后置通知,为什么会是最终通知?因为程序的调用问题,可以测试的,弄个异常,运行的时候,后置通知就不会执行。
3.推荐使用around(环绕通知),它可以控制通知的顺序,可以手动控制通知
完全使用注解,去掉xml文件
目录结构中去掉beans.xml。新增一个类
SpringConfiguration.java
package com.zh.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.zh.aop")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
修改Test_aop.java
使用 AnnotationConfigApplicationContext 而不是 ClassPathXmlApplicationContext
package com.zh.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test_aop {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//aop的底层原理是动态代理实现增强,有两种实现,一种是接口代理,另一种是cglib子类代理
//所以这里必须要使用接口,而且必须使用接口来接收
IUser user1 = ac.getBean("getUser",IUser.class);
user1.add();
}
}
运行效果一样
项目源码