准备POJO
package com.bob.analyst.admin.aop;
import org.springframework.stereotype.Component;
@Component
public class Role {
private Long id;
private String roleName;
private String note;
//setter and getter 省略
}
选择切点(Service和ServiceImpl)
Service:
package com.bob.analyst.admin.aop;
public interface RoleService {
public void printRole(Role role);
}
ServiceImpl:
package com.bob.analyst.admin.aop;
import org.springframework.stereotype.Component;
@Component
public class RoleServiceImpl implements RoleService{
@Override
public void printRole(Role role) {
System.out.println("{id:"+role.getId()+","
+"role_name:"+role.getRoleName()+","
+"note:"+role.getNote()+"}");
}
}
创建切面
选择好切点就可以创建切面了,对于动态代理的概念而言,它就如同一个拦截器,在Spring中使用@Aspect注解一个类,那么Spring Ioc容器就会认为这是一个切面了。
package com.bob.analyst.admin.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.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class RoleAspect {
/**
* execution 代表执行方法的时候会触发 * 代表任意返回类型的方法
* com.bob.analyst.service.impl.RoleServiceImpl 代表类的全限定名 printRole 被拦截方法名称
* (..) 任意的参数
*/
@Pointcut("execution(* com.bob.analyst.admin.aop.RoleServiceImpl.printRole(..))")
public void print() {
}
// 前置通知
// @Before("execution(*
// com.bob.analyst.admin.aop.RoleServiceImpl.printRole(..))")
@Before("print()")
public void before() {
System.out.println("before.....");
}
// 后置通知
// @After("execution(*
// com.bob.analyst.admin.aop.RoleServiceImpl.printRole(..))")
@After("print()")
public void after() {
System.out.println("after.....");
}
// 返回通知(无异常)
// @AfterReturning("execution(*
// com.bob.analyst.admin.aop.RoleServiceImpl.printRole(..))")
@AfterReturning("print()")
public void afterReturning() {
System.out.println("afterReturning.....");
}
// 异常通知
// @AfterThrowing("execution(*
// com.bob.analyst.admin.aop.RoleServiceImpl.printRole(..))")
@AfterThrowing("print()")
public void afterThrowing() {
System.out.println("afterThrowing.....");
}
}
连接点
在切面的类中每个方法上面的注解就是连接点的相关信息
测试AOP
即把之前的类注入到Spring Ioc容器中,然后运行
package com.bob.analyst.admin.aop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.bob.analyst.admin.aop")
public class AopConfig {
@Bean
public RoleAspect getRoleAspect(){
return new RoleAspect();
}
}
package com.bob.analyst.admin.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService= (RoleService) ctx.getBean(RoleService.class);
Role role = new Role();
role.setId(1l);
role.setRoleName("高级软件工程师");
role.setNote("note_01");
roleService.printRole(role);
System.out.println("........................");
role=null;
roleService.printRole(role);
}
}
结果:
环绕通知
环绕通知是Spring AOP中最强大的通知,它可以同时实现前置通知和后置通知。它保留了调度被代理对象原有方法的功能,所以它既强大又灵活。实现:在RoleAspect类中加入以下代码:
/**
* 环绕通知
*
* @param jp
* 这个参数是Spring提供的,使用它可以反射切点方法。
*/
@Around("print()")
public void around(ProceedingJoinPoint jp) {
System.out.println("around before ....");
try {
jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around after ....");
}
在运行测试类main得出结果:
织入
织入是生成代理对象的过程,在上述的代码中,切点方法所在的类都是拥有接口的类,而事实上即使没有接口,Spring也能提供AOP的功能,所以是否拥有接口不是使用Spring AOP的一个强制要求,Spring提供了一个规则:当类的实现存在接口的时候,Spring将提供JDK动态代理,从而织入各个通知。而当类不存在接口的时候就没办法使用JDK动态代理,Spring会使用CGLIB来生成代理对象。备注:用Spring建议使用接口编程
引入
Spring AOP只是通过动态代理技术,把各类通过通知织入到它所约定的流程当中,而事实上,有时候我们希望引入其他类的方法来得到更好的实现,这时候就可以引入其他的方法了。先添加接口和实现:
package com.bob.analyst.admin.aop;
public interface RoleVerifier {
public boolean verify(Role role);
}
package com.bob.analyst.admin.aop;
public class RoleVerifierImpl implements RoleVerifier {
@Override
public boolean verify(Role role) {
System.out.println("验证Role是否为空........");
return role != null;
}
}
在RoleAspect类中添加
/**
* com.bob.analyst.admin.aop.RoleServiceImpl+ :
* 表示对RoleServiceImpl类进行增强,也就是在RoleServiceImpl中引入一个新的接口 defaultImpl :
* 代表其默认的实现类,这里是RoleVerifierImpl
*/
@DeclareParents(value = "com.bob.analyst.admin.aop.RoleServiceImpl+", defaultImpl = RoleVerifierImpl.class)
public RoleVerifier roleVerifier;
更改测试
package com.bob.analyst.admin.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService= (RoleService) ctx.getBean(RoleService.class);
/**
* 强制转换 把roleService转换为roleVerifier接口对象,然后就可以使用verify方法了。
* Spring AOP依赖于动态代理对象来实现,其代理对象可以挂在多个接口之下,只要Spring AOP让代理对象挂到RoleService 和 RoleVerifier
* 两个接口之下,那么就可以把对应的Bean通过强制转换,让其在这两个接口之间相互转换了。
*/
RoleVerifier roleVerifier = (RoleVerifier) roleService;
Role role = new Role();
role.setId(1l);
role.setRoleName("高级软件工程师");
role.setNote("note_01");
if(roleVerifier.verify(role)){
roleService.printRole(role);
}
System.out.println("........................");
role=null;
roleService.printRole(role);
}
}
结果:
关于使用XML配置开发Spring AOP这里不再描述。。。