相關
- 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文件
<?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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 由Spring 上下文 創建并裝配 所有的對象-->
<bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
<bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
<bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
<bean id="service" class="spring4.ServiceImpl"/>
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
<value>afterAdvice</value>
<value>roundAdvice</value>
</list>
</property>
<property name="target" ref="service"></property>
</bean>
</beans>
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就擁有了描述某些類的某些特定方法的能力。
<?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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
<bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
<bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
<bean id="service" class="spring4.ServiceImpl"/>
<!--通過正則表達定義增強具體的執行位置-->
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"/>
<!--正則表達式用來表示增加添加到哪些類的哪些方法-->
<property name="pattern" value="spring4\..*.print.*"/><!--表示應用到spring4包下所有類中的所有print開頭的方法上-->
</bean>
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>advisor</value><!--連接到具體執行位置-->
</list>
</property>
<property name="target" ref="service"></property>
</bean>
</beans>
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
<?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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
<bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
<bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
<bean id="service" class="spring4.ServiceImpl"/>
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"/>
<!--正則表達式用來表示增加添加到哪些類的哪些方法-->
<property name="pattern" value="spring4\..*.print.*"/><!--表示應用到spring4包下所有類中的所有print開頭的方法上-->
</bean>
<!--會掃描所有容器中的advisor, 如上面定義的 advisor, 這個advisor 可以匹配到 若干個類
若出現 advisor2, advisor3, advisor4 得寫 相應個數 proxyFactoryBean
然后自動為這些advisor要應用的bean生成代理-->
<!--bean 即要被增強的 target 類-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
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");
}
}