AOP(Aspect-Oriented Programming: 面向切面编程):
将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为“切面”(Aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性;
Spring AOP 基于动态代理实现:
○ 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
○ 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);
AOP通知类型
AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分
前置通知(Before)
返回通知(After-returning)
异常通知(After-throwing)
后置通知(After)
环绕通知(Around)
AOP连接点(Join point)
AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点
AOP切点(Pointcut)
AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集
AOP目标对象(Target):
就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的
AOP织入(Weaving):
就是将挖掉的功能回填的动态过程
AOP切面:切点+通知
SpringAOP+AspectJ实现步骤
1.添加依赖,aop与aspectj表达式的依赖
2.创建spring的主配置文件,bean内的命名空间要添加aop的
3.创建业务代码并编写日志记录代码(事务管理代码)
4.将业务层与日志记录层注入spring容器
5.<aop:config>--aop配置
aop:aspect--aop切面
aop:before--通知内容与通知类型
切点表达式配置语法:
execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
eg:
execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意
execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数
execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意
execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
execution(* *...*.*(..))
如果有参数
int======>int
String===>java.lang.String
代码示例
public interface IAccountService {
/**
* 新增
* */
public void save(int i);
/**
* 修改
* */
public void update();
/**
* 删除
* */
public int delete();
}
//实现类
public class AccountServiceImpl implements IAccountService{
@Override
public void save(int i) {
System.out.println("业务层的新增方法:"+i);
// int a=10/0;//模拟异常
}
@Override
public void update() {
System.out.println("业务层的修改方法:");
}
@Override
public int delete() {
System.out.println("业务层的删除方法:");
return 0;
}
}
public class Logger {
public void beforeMethod(){
System.out.println("日志的前置通知");
}
public void returnMethod(){
System.out.println("日志的返回通知");
}
public void throwMethod(){
System.out.println("日志的异常通知");
}
public void afterMethod(){
System.out.println("日志的后置通知");
}
public Object aroundMethod(ProceedingJoinPoint pjp){
Object obj=null;
try{
System.out.println("环绕通知===前置通知");
//切点方法
Object[] ars= pjp.getArgs();
obj=pjp.proceed(ars);
System.out.println("环绕通知===返回通知");
}catch (Throwable t){
t.printStackTrace();
System.out.println("环绕通知===异常通知");
}finally {
System.out.println("环绕通知===后置通知");
return obj;
}
}
}
//applicationContext.xml类
<!--注入业务层-->
<bean id="service" class="com.apesource.service.AccountServiceImpl"></bean>
<!--注入日志记录层(通知)-->
<bean id="logger" class="com.apesource.util.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="aopAspect" ref="logger">
<!--切点-->
<aop:pointcut id="dian" expression="execution(* com.apesource.service.*.*(..))"/>
<!--通知-->
<!--<aop:before method="beforeMethod" pointcut-ref="dian"></aop:before>
<aop:after-returning method="returnMethod" pointcut-ref="dian"></aop:after-returning>
<aop:after-throwing method="throwMethod" pointcut-ref="dian"></aop:after-throwing>
<aop:after method="afterMethod" pointcut-ref="dian"></aop:after>-->
<!--环绕通知-->
<aop:around method="aroundMethod" pointcut-ref="dian"></aop:around>
</aop:aspect>
</aop:config>
//Test类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test03 {
@Autowired
public IAccountService service;
@Test
public void test1(){
service.save(1);
System.out.println("-------------------------");
service.update();
System.out.println("-------------------------");
service.delete();
System.out.println("-------------------------");
}
}
运行结果: