相关
- Spring支持5种类型的增强或通知(advice)
- before、around、after、afterReturnning
前提
- Intellij
- 创建Maven archtype webapp
- SpringMVC 项目 而非 spring boot
- 添加 spring-webmvn dependency
案例
service 业务逻辑层
public interface Service {
void print(String str);
//void say(String str);
}
public class ServiceImpl implements Service {
public void print(String str) {
System.out.println("我是业务方法"+str);
}
//public void say(String str) {
// System.out.println("222"+str);
//}
}
aop 定义增强业务
** before 增强 **
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* Created by weixin:javajidi_com.
* 方法执行前的逻辑,称前置通知,
* 通过编程实现接口
*/
public class BeforeAdvice implements MethodBeforeAdvice{
/**
* method: target 方法
* objects: target 方法需要的参数
* o:目的所要增强的 target 对象
**/
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("BeforeAdvice方法执行前");
System.out.println(method.getName()+";"+o.getClass());
}
}
round 环绕增强
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 实现接口为 MethodInterceptor extends Advice
*/
public class RoundAdvice implements MethodInterceptor {
//注意参数为:MethodInvocation
//通过 它可以拿到参数信息
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Roundadvice方法执行前");
System.out.println(methodInvocation.getArguments()[0]);//可以获取目标方法的参数值
Object result=methodInvocation.proceed();//调用目标对象的方法
System.out.println("RoundAdvice方法执行完成了");
return result;
}
}
afterreturnning 增强
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//实现 接口
public class AfterAdvice implements AfterReturningAdvice {
/**
* o: target 方法执行返回结果
* method: target 方法
* objects: target 方法参数
* o1: 要增强 的 target 对象
**/
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("AfterAdvice方法执行完成了");
System.out.println(method.getName()+";"+o1.getClass());
}
}
Test1
import org.springframework.aop.framework.ProxyFactory;
import spring4.Service;
import spring4.ServiceImpl;
/**
* test1: 不通过 spring bean 配置文件
*/
public class Test {
public static void main(String[] arg){
//非 Spring上下文 控制 bean, 全部自定义(new)
Service service=new ServiceImpl();//
//使用代理工厂为目标对象创建代理,
//我们自己的advice逻辑
ProxyFactory proxyFactoryBean=new ProxyFactory();
proxyFactoryBean.setTarget(service);//设置目标对象
BeforeAdvice beforeAdvice = new BeforeAdvice();
proxyFactoryBean.addAdvice(beforeAdvice);//为目标对象织入增强
AfterAdvice afterAdvice = new AfterAdvice();
proxyFactoryBean.addAdvice(afterAdvice);
RoundAdvice roundAdvice = new RoundAdvice();
proxyFactoryBean.addAdvice(roundAdvice);
Service proxy=(Service)proxyFactoryBean.getProxy();
proxy.print("test");
}
}
classpath 路径下配置 spring.xml 实现
//在resources 目录 创建spring.xml文件
beforeAdvice
afterAdvice
roundAdvice
test2 通过bean 配置文件和Spring上下文
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;
public class Test {
public static void main(String[] arg){
// Service service=new ServiceImpl();
// //使用代理工厂为目标对象创建代理,并织入我们自己的advice逻辑
// ProxyFactory proxyFactoryBean=new ProxyFactory();
// proxyFactoryBean.setTarget(service);//设置目标对象
// proxyFactoryBean.addAdvice(new BeforeAdvice());//为目标对象织入增强
// proxyFactoryBean.addAdvice(new AfterAdvice());
// proxyFactoryBean.addAdvice(new RoundAdvice());
// Service proxy=(Service)proxyFactoryBean.getProxy();
// proxy.print("test");
//获取上下文中的bean
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
Service service=applicationContext.getBean("serviceProxy",Service.class);
service.print("test");
}
}
**运行结果-增强被织入到目标类的所有方法中 **
BeforeAdvice方法执行前
print;class spring4.ServiceImpl
Roundadvice方法执行前
test
我是业务方法test
RoundAdvice方法执行完成了
AfterAdvice方法执行完成了
print;class spring4.ServiceImpl
更细粒度-指定不同方法的增强
- spring 通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成。
- 通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上
- 这样Pointcut就拥有了描述某些类的某些特定方法的能力。
advisor
test3
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;
/**
* Created by weixin:javajidi_com.
*/
public class Test {
public static void main(String[] arg){
// Service service=new ServiceImpl();
// //使用代理工厂为目标对象创建代理,并织入我们自己的advice逻辑
// ProxyFactory proxyFactoryBean=new ProxyFactory();
// proxyFactoryBean.setTarget(service);//设置目标对象
// proxyFactoryBean.addAdvice(new BeforeAdvice());//为目标对象织入增强
// proxyFactoryBean.addAdvice(new AfterAdvice());
// proxyFactoryBean.addAdvice(new RoundAdvice());
// Service proxy=(Service)proxyFactoryBean.getProxy();
// proxy.print("test");
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
//直接从上下文获取 proxy
Service service=applicationContext.getBean("serviceProxy",Service.class);
service.print("print");
//添加多余验证代码,验证其不会被增强
service.say("say");
}
}
结果
BeforeAdvice方法执行前
print;class spring4.ServiceImpl
我是业务方法print
say:say
动态生成 proxy
- 当有很多 目的target 类需要被增强时,需要手动配置很多 proxy
- Spring 提供自动代理机制
- 在内部Spring 使用 BeanPostProcessor 自动完成此任务
- BeanPostProcessor 的实现类: 根据 某些规则 自动在容器实例化 bean时为符合规则条件的bean 生成代理
- BeanNameAutoProxyCreator
- 允许为一组特定配置名的Bean自动创建代理
- DefaultAdvisorAutoProxyCreator
- 它会对容器中所有的Advisor进行扫描,自动将这些切面应用到匹配的Bean中(即为目标Bean创建代理实例)
- AnnotationAwareAspectjAutoProxyCreator
- 为包含AspectJ注解的Bean自动创建代理实例
- BeanNameAutoProxyCreator
test 4
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;
/**
* Created by weixin:javajidi_com.
*/
public class Test {
public static void main(String[] arg){
// Service service=new ServiceImpl();
// //使用代理工厂为目标对象创建代理,并织入我们自己的advice逻辑
// ProxyFactory proxyFactoryBean=new ProxyFactory();
// proxyFactoryBean.setTarget(service);//设置目标对象
// proxyFactoryBean.addAdvice(new BeforeAdvice());//为目标对象织入增强
// proxyFactoryBean.addAdvice(new AfterAdvice());
// proxyFactoryBean.addAdvice(new RoundAdvice());
// Service proxy=(Service)proxyFactoryBean.getProxy();
// proxy.print("test");
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
//要注意这里 bean name,可以debug,
//applicationContext.getBeanNamesForType(Service.class) 拿到 bean 名字
Service service=applicationContext.getBean("service",Service.class);
service.print("print");
service.say("say");
}
}
参考: https://www.jianshu.com/p/1dd6a26c881b