Spring-AOP 是对 AOP框架之一。其他比如还有AspectJ
Aspect-Oriented-Programming(面向切面编程),一种编程思想。
切面:Aspect,由
切入点
和额外功能(增强)
组成。作用:提供了新的编程角度,不再只是考虑
类、对象
,而可以考虑切面
。切面和目标形成代理
,解决项目业务中额外功 能冗余的问题。
1、为什么会有Aop(面向切面编程)的思想?
业务层存在问题,业务层的组成为核心代码+额外功能,从而存在这大量的代码重复,增加了代码的冗余,是项目维护存在了极大隐患。从而出现了两种代理
(1)静态代理
另外写一个类继承与核心代码相同的接口,在内部新建一个核心代码部分,在内部进行修饰(个人感觉与装饰者模式差不多)。
UserService us=new UserServiceImpl();
public void updateUser(User user){
System.out.println("事务管理功能"); //代理类负责额外功能
us.updateUser(user); // 目标自己负责核心功能
}
public void inserUser(User user){
System.out.println("事务管理功能");//代理类负责额外功能
us.insertUser(user);// 目标自己负责核心功能
}
public User queryUser(Integer id){
System.out.println("事务管理功能");//代理类负责额外功能
us.queryUser(user);// 目标自己负责核心功能
}
(2)动态代理
动态代理分为两种
1、jdk代理要求代理类和核心类实现了相同的接口。
2、cglib动态代理( 第三方cglib库中的一套api ) 通过继承目标保证功能一致
Spring中的aop章节底部采用了这两种代理的方式实现,并对动态代理提供了简单的、可操作性强的决绝方案。
1、在pom.xml中添加依赖
org.springframework
spring-context-support
4.3.6.RELEASE
org.springframework
spring-aspects
4.3.6.RELEASE
1、创建target
@Override
public Integer InsertUser(User user) {
System.out.println("核心功能");
return 4;
}
@Override
public User findQueryOne(Integer id) {
System.out.println("核心功能");
return new User();
}
}
2、创建advice
这里有五种
1、MethodBeforeAdvice在核心代码运行之前运行
//method 当前执行的方法对象
//args方法的参数
//target 目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(new Date().toLocaleString()+" "+method.getName()+"被访问");
}
}
简单打印日志文件
2、AfterReturningAdvice 在核心代码之后运行
//returnValue核心业务返回值
//Method方法对象
//args方法的参数表
//target目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("return value = "+returnValue);
System.out.println("method = "+method.getName());
}
}
3、MethodInterceptor 在核心代码之间环绕(前后都执行,如果仅仅是之前或者之后,尽量不要用这个替代前两者,这点Spring官方明确规定)
/*环绕
* MethodInvocation
* */
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Interceptor1");
Object proceed = invocation.proceed();//执行目标的核心功能
System.out.println("Interceptor2");
return proceed;//业务结果向上返回
}
}
4、ThrowsAdvice异常额外功能(了解)
//目标业务方法中抛出异常时,执行此方法。ex=抛出的异常对象
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage()+"~~~");
}
}
5、AfterAdvice(了解)与AfterRetuenAdvice用法相似
1、切入点表达式
(1)execution
1> * com.service.UserServiceImpl.queryUser(..)
修饰符:任意
返回值:任意
包:com.service
类:UserServiceImpl
方法:queryUser
参数表:任意
2> * com.service.UserServiceImpl.*(..)
修饰符:任意
返回值:任意
包:com.service
类:UserServiceImpl
方法:所有,任意
参数表:任意
3> * com..UserServiceImpl.*(..)
修饰符:任意
返回值:任意
包:com包,及其子包
类:UserServiceImpl
方法:所有,任意
参数表:任意
4> * com.service.*.*(..)
修饰符:任意
返回值:任意
包:com.service
类:所有,任意
方法:所有,任意
参数表:任意
5> * *(..) 不建议
修饰符:任意
返回值:任意
包:任意
类:所有,任意
方法:所有,任意
参数表:任意
6> * com.service.UserServiceImpl.query*(..) 【技巧:批量切入】
修饰符:任意
返回值:任意
包:com.service
类:UserServiceImpl
方法:所有,任意
参数表:任意
*注意:尽量精确,避免不必要的切入
切入点表达式只会拦截在配置文件中配置的类型对象。
2、within(描述 包、类、以及类下所有方法的切入)
within(包)
within(类)
3、orgs(符合方法插入、描述符合该参数的所有方法)
args(int,String,com.entity.User) 参数表如此的方法
用法和基本用法相同。
测试类:
@Override
public Integer InsertUser(User user) {
System.out.println("核心功能");
return 4;
}
@Override
public User findQueryOne(Integer id) {
System.out.println("核心功能");
return new User();
}
}
@Test
public void test1(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userservice1 = classPathXmlApplicationContext.getBean("userservice", UserService.class);
userservice1.InsertUser(new User());
userservice1.findQueryOne(1);
}
}